diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/angle/checkout/src/libANGLE/Context.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/Context.cpp')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/Context.cpp | 10472 |
1 files changed, 10472 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/Context.cpp b/gfx/angle/checkout/src/libANGLE/Context.cpp new file mode 100644 index 0000000000..bfd490ab29 --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/Context.cpp @@ -0,0 +1,10472 @@ +// +// Copyright 2002 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// Context.cpp: Implements the gl::Context class, managing all GL state and performing +// rendering operations. It is the GLES2 specific implementation of EGLContext. +#include "libANGLE/Context.inl.h" + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include <iterator> +#include <sstream> +#include <vector> + +#include "common/PackedEnums.h" +#include "common/angle_version_info.h" +#include "common/matrix_utils.h" +#include "common/platform.h" +#include "common/system_utils.h" +#include "common/utilities.h" +#include "libANGLE/Buffer.h" +#include "libANGLE/Compiler.h" +#include "libANGLE/Display.h" +#include "libANGLE/Fence.h" +#include "libANGLE/FramebufferAttachment.h" +#include "libANGLE/MemoryObject.h" +#include "libANGLE/PixelLocalStorage.h" +#include "libANGLE/Program.h" +#include "libANGLE/ProgramPipeline.h" +#include "libANGLE/Query.h" +#include "libANGLE/Renderbuffer.h" +#include "libANGLE/ResourceManager.h" +#include "libANGLE/Sampler.h" +#include "libANGLE/Semaphore.h" +#include "libANGLE/Surface.h" +#include "libANGLE/Texture.h" +#include "libANGLE/TransformFeedback.h" +#include "libANGLE/VertexArray.h" +#include "libANGLE/capture/FrameCapture.h" +#include "libANGLE/capture/frame_capture_utils.h" +#include "libANGLE/formatutils.h" +#include "libANGLE/queryconversions.h" +#include "libANGLE/queryutils.h" +#include "libANGLE/renderer/DisplayImpl.h" +#include "libANGLE/renderer/Format.h" +#include "libANGLE/validationES.h" + +#if defined(ANGLE_PLATFORM_APPLE) +# include <dispatch/dispatch.h> +# include "common/tls.h" +#endif + +namespace gl +{ +namespace +{ + +egl::ShareGroup *AllocateOrGetShareGroup(egl::Display *display, const gl::Context *shareContext) +{ + if (shareContext) + { + egl::ShareGroup *shareGroup = shareContext->getState().getShareGroup(); + shareGroup->addRef(); + return shareGroup; + } + else + { + return new egl::ShareGroup(display->getImplementation()); + } +} + +template <typename T> +angle::Result GetQueryObjectParameter(const Context *context, Query *query, GLenum pname, T *params) +{ + if (!query) + { + // Some applications call into glGetQueryObjectuiv(...) prior to calling glBeginQuery(...) + // This wouldn't be an issue since the validation layer will handle such a usecases but when + // the app enables EGL_KHR_create_context_no_error extension, we skip the validation layer. + switch (pname) + { + case GL_QUERY_RESULT_EXT: + *params = 0; + break; + case GL_QUERY_RESULT_AVAILABLE_EXT: + *params = GL_FALSE; + break; + default: + UNREACHABLE(); + return angle::Result::Stop; + } + return angle::Result::Continue; + } + + switch (pname) + { + case GL_QUERY_RESULT_EXT: + return query->getResult(context, params); + case GL_QUERY_RESULT_AVAILABLE_EXT: + { + bool available = false; + if (context->isContextLost()) + { + available = true; + } + else + { + ANGLE_TRY(query->isResultAvailable(context, &available)); + } + *params = CastFromStateValue<T>(pname, static_cast<GLuint>(available)); + return angle::Result::Continue; + } + default: + UNREACHABLE(); + return angle::Result::Stop; + } +} + +// Attribute map queries. +EGLint GetClientMajorVersion(const egl::AttributeMap &attribs) +{ + return static_cast<EGLint>(attribs.get(EGL_CONTEXT_CLIENT_VERSION, 1)); +} + +EGLint GetClientMinorVersion(const egl::AttributeMap &attribs) +{ + return static_cast<EGLint>(attribs.get(EGL_CONTEXT_MINOR_VERSION, 0)); +} + +bool GetBackwardCompatibleContext(const egl::AttributeMap &attribs) +{ + return attribs.get(EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_TRUE) == EGL_TRUE; +} + +bool GetWebGLContext(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE, EGL_FALSE) == EGL_TRUE); +} + +Version GetClientVersion(egl::Display *display, + const egl::AttributeMap &attribs, + const EGLenum clientType) +{ + Version requestedVersion = + Version(GetClientMajorVersion(attribs), GetClientMinorVersion(attribs)); + if (GetBackwardCompatibleContext(attribs)) + { + if (clientType == EGL_OPENGL_API) + { + Optional<gl::Version> maxSupportedDesktopVersion = + display->getImplementation()->getMaxSupportedDesktopVersion(); + if (maxSupportedDesktopVersion.valid()) + return std::max(maxSupportedDesktopVersion.value(), requestedVersion); + else + return requestedVersion; + } + else if (requestedVersion.major == 1) + { + // If the user requests an ES1 context, we cannot return an ES 2+ context. + return Version(1, 1); + } + else + { + // Always up the version to at least the max conformant version this display supports. + // Only return a higher client version if requested. + const Version conformantVersion = std::max( + display->getImplementation()->getMaxConformantESVersion(), requestedVersion); + // Limit the WebGL context to at most version 3.1 + const bool isWebGL = GetWebGLContext(attribs); + return isWebGL ? std::min(conformantVersion, Version(3, 1)) : conformantVersion; + } + } + else + { + return requestedVersion; + } +} + +EGLint GetProfileMask(const egl::AttributeMap &attribs) +{ + return attribs.getAsInt(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT); +} + +GLenum GetResetStrategy(const egl::AttributeMap &attribs) +{ + EGLAttrib resetStrategyExt = + attribs.get(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, EGL_NO_RESET_NOTIFICATION); + EGLAttrib resetStrategyCore = + attribs.get(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY, resetStrategyExt); + + switch (resetStrategyCore) + { + case EGL_NO_RESET_NOTIFICATION: + return GL_NO_RESET_NOTIFICATION_EXT; + case EGL_LOSE_CONTEXT_ON_RESET: + return GL_LOSE_CONTEXT_ON_RESET_EXT; + default: + UNREACHABLE(); + return GL_NONE; + } +} + +bool GetRobustAccess(const egl::AttributeMap &attribs) +{ + EGLAttrib robustAccessExt = attribs.get(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_FALSE); + EGLAttrib robustAccessCore = attribs.get(EGL_CONTEXT_OPENGL_ROBUST_ACCESS, robustAccessExt); + + bool attribRobustAccess = (robustAccessCore == EGL_TRUE); + bool contextFlagsRobustAccess = + ((attribs.get(EGL_CONTEXT_FLAGS_KHR, 0) & EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR) != 0); + + return (attribRobustAccess || contextFlagsRobustAccess); +} + +bool GetDebug(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_OPENGL_DEBUG, EGL_FALSE) == EGL_TRUE) || + ((attribs.get(EGL_CONTEXT_FLAGS_KHR, 0) & EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR) != 0); +} + +bool GetNoError(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, EGL_FALSE) == EGL_TRUE); +} + +bool GetExtensionsEnabled(const egl::AttributeMap &attribs, bool webGLContext) +{ + // If the context is WebGL, extensions are disabled by default + EGLAttrib defaultValue = webGLContext ? EGL_FALSE : EGL_TRUE; + return (attribs.get(EGL_EXTENSIONS_ENABLED_ANGLE, defaultValue) == EGL_TRUE); +} + +bool GetBindGeneratesResource(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM, EGL_TRUE) == EGL_TRUE); +} + +bool GetClientArraysEnabled(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE) == EGL_TRUE); +} + +bool GetRobustResourceInit(egl::Display *display, const egl::AttributeMap &attribs) +{ + const angle::FrontendFeatures &frontendFeatures = display->getFrontendFeatures(); + return (frontendFeatures.forceRobustResourceInit.enabled || + attribs.get(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE, EGL_FALSE) == EGL_TRUE); +} + +EGLenum GetContextPriority(const egl::AttributeMap &attribs) +{ + return static_cast<EGLenum>( + attribs.getAsInt(EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_MEDIUM_IMG)); +} + +bool GetProtectedContent(const egl::AttributeMap &attribs) +{ + return static_cast<bool>(attribs.getAsInt(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE)); +} + +std::string GetObjectLabelFromPointer(GLsizei length, const GLchar *label) +{ + std::string labelName; + if (label != nullptr) + { + size_t labelLength = length < 0 ? strlen(label) : length; + labelName = std::string(label, labelLength); + } + return labelName; +} + +void GetObjectLabelBase(const std::string &objectLabel, + GLsizei bufSize, + GLsizei *length, + GLchar *label) +{ + size_t writeLength = objectLabel.length(); + if (label != nullptr && bufSize > 0) + { + writeLength = std::min(static_cast<size_t>(bufSize) - 1, objectLabel.length()); + std::copy(objectLabel.begin(), objectLabel.begin() + writeLength, label); + label[writeLength] = '\0'; + } + + if (length != nullptr) + { + *length = static_cast<GLsizei>(writeLength); + } +} + +enum SubjectIndexes : angle::SubjectIndex +{ + kTexture0SubjectIndex = 0, + kTextureMaxSubjectIndex = kTexture0SubjectIndex + IMPLEMENTATION_MAX_ACTIVE_TEXTURES, + kImage0SubjectIndex = kTextureMaxSubjectIndex, + kImageMaxSubjectIndex = kImage0SubjectIndex + IMPLEMENTATION_MAX_IMAGE_UNITS, + kUniformBuffer0SubjectIndex = kImageMaxSubjectIndex, + kUniformBufferMaxSubjectIndex = + kUniformBuffer0SubjectIndex + IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS, + kAtomicCounterBuffer0SubjectIndex = kUniformBufferMaxSubjectIndex, + kAtomicCounterBufferMaxSubjectIndex = + kAtomicCounterBuffer0SubjectIndex + IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, + kShaderStorageBuffer0SubjectIndex = kAtomicCounterBufferMaxSubjectIndex, + kShaderStorageBufferMaxSubjectIndex = + kShaderStorageBuffer0SubjectIndex + IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS, + kSampler0SubjectIndex = kShaderStorageBufferMaxSubjectIndex, + kSamplerMaxSubjectIndex = kSampler0SubjectIndex + IMPLEMENTATION_MAX_ACTIVE_TEXTURES, + kVertexArraySubjectIndex = kSamplerMaxSubjectIndex, + kReadFramebufferSubjectIndex, + kDrawFramebufferSubjectIndex, + kProgramPipelineSubjectIndex, +}; + +bool IsClearBufferEnabled(const FramebufferState &fbState, GLenum buffer, GLint drawbuffer) +{ + return buffer != GL_COLOR || fbState.getEnabledDrawBuffers()[drawbuffer]; +} + +bool IsEmptyScissor(const State &glState) +{ + if (!glState.isScissorTestEnabled()) + { + return false; + } + + const Extents &dimensions = glState.getDrawFramebuffer()->getExtents(); + Rectangle framebufferArea(0, 0, dimensions.width, dimensions.height); + return !ClipRectangle(framebufferArea, glState.getScissor(), nullptr); +} + +bool IsColorMaskedOut(const BlendStateExt &blendStateExt, const GLint drawbuffer) +{ + ASSERT(static_cast<size_t>(drawbuffer) < blendStateExt.getDrawBufferCount()); + return blendStateExt.getColorMaskIndexed(static_cast<size_t>(drawbuffer)) == 0; +} + +bool GetIsExternal(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_EXTERNAL_CONTEXT_ANGLE, EGL_FALSE) == EGL_TRUE); +} + +bool GetSaveAndRestoreState(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_EXTERNAL_CONTEXT_SAVE_STATE_ANGLE, EGL_FALSE) == EGL_TRUE); +} + +void GetPerfMonitorString(const std::string &name, + GLsizei bufSize, + GLsizei *length, + GLchar *stringOut) +{ + GLsizei numCharsWritten = std::min(bufSize, static_cast<GLsizei>(name.size())); + + if (length) + { + if (bufSize == 0) + { + *length = static_cast<GLsizei>(name.size()); + } + else + { + // Excludes null terminator. + ASSERT(numCharsWritten > 0); + *length = numCharsWritten - 1; + } + } + + if (stringOut) + { + memcpy(stringOut, name.c_str(), numCharsWritten); + } +} + +bool CanSupportAEP(const gl::Version &version, const gl::Extensions &extensions) +{ + // From the GL_ANDROID_extension_pack_es31a extension spec: + // OpenGL ES 3.1 and GLSL ES 3.10 are required. + // The following extensions are required: + // * KHR_debug + // * KHR_texture_compression_astc_ldr + // * KHR_blend_equation_advanced + // * OES_sample_shading + // * OES_sample_variables + // * OES_shader_image_atomic + // * OES_shader_multisample_interpolation + // * OES_texture_stencil8 + // * OES_texture_storage_multisample_2d_array + // * EXT_copy_image + // * EXT_draw_buffers_indexed + // * EXT_geometry_shader + // * EXT_gpu_shader5 + // * EXT_primitive_bounding_box + // * EXT_shader_io_blocks + // * EXT_tessellation_shader + // * EXT_texture_border_clamp + // * EXT_texture_buffer + // * EXT_texture_cube_map_array + // * EXT_texture_sRGB_decode + return (version >= ES_3_1 && extensions.debugKHR && extensions.textureCompressionAstcLdrKHR && + extensions.blendEquationAdvancedKHR && extensions.sampleShadingOES && + extensions.sampleVariablesOES && extensions.shaderImageAtomicOES && + extensions.shaderMultisampleInterpolationOES && extensions.textureStencil8OES && + extensions.textureStorageMultisample2dArrayOES && extensions.copyImageEXT && + extensions.drawBuffersIndexedEXT && extensions.geometryShaderEXT && + extensions.gpuShader5EXT && extensions.primitiveBoundingBoxEXT && + extensions.shaderIoBlocksEXT && extensions.tessellationShaderEXT && + extensions.textureBorderClampEXT && extensions.textureBufferEXT && + extensions.textureCubeMapArrayEXT && extensions.textureSRGBDecodeEXT); +} +} // anonymous namespace + +#if defined(ANGLE_PLATFORM_APPLE) +// TODO(angleproject:6479): Due to a bug in Apple's dyld loader, `thread_local` will cause +// excessive memory use. Temporarily avoid it by using pthread's thread +// local storage instead. +static TLSIndex GetCurrentValidContextTLSIndex() +{ + static TLSIndex CurrentValidContextIndex = TLS_INVALID_INDEX; + static dispatch_once_t once; + dispatch_once(&once, ^{ + ASSERT(CurrentValidContextIndex == TLS_INVALID_INDEX); + CurrentValidContextIndex = CreateTLSIndex(nullptr); + }); + return CurrentValidContextIndex; +} +Context *GetCurrentValidContextTLS() +{ + TLSIndex CurrentValidContextIndex = GetCurrentValidContextTLSIndex(); + ASSERT(CurrentValidContextIndex != TLS_INVALID_INDEX); + return static_cast<Context *>(GetTLSValue(CurrentValidContextIndex)); +} +void SetCurrentValidContextTLS(Context *context) +{ + TLSIndex CurrentValidContextIndex = GetCurrentValidContextTLSIndex(); + ASSERT(CurrentValidContextIndex != TLS_INVALID_INDEX); + SetTLSValue(CurrentValidContextIndex, context); +} +#else +thread_local Context *gCurrentValidContext = nullptr; +#endif + +Context::Context(egl::Display *display, + const egl::Config *config, + const Context *shareContext, + TextureManager *shareTextures, + SemaphoreManager *shareSemaphores, + MemoryProgramCache *memoryProgramCache, + MemoryShaderCache *memoryShaderCache, + const EGLenum clientType, + const egl::AttributeMap &attribs, + const egl::DisplayExtensions &displayExtensions, + const egl::ClientExtensions &clientExtensions) + : mState(shareContext ? &shareContext->mState : nullptr, + AllocateOrGetShareGroup(display, shareContext), + shareTextures, + shareSemaphores, + &mOverlay, + clientType, + GetClientVersion(display, attribs, clientType), + GetProfileMask(attribs), + GetDebug(attribs), + GetBindGeneratesResource(attribs), + GetClientArraysEnabled(attribs), + GetRobustResourceInit(display, attribs), + memoryProgramCache != nullptr, + GetContextPriority(attribs), + GetRobustAccess(attribs), + GetProtectedContent(attribs)), + mShared(shareContext != nullptr || shareTextures != nullptr || shareSemaphores != nullptr), + mSkipValidation(GetNoError(attribs)), + mDisplayTextureShareGroup(shareTextures != nullptr), + mDisplaySemaphoreShareGroup(shareSemaphores != nullptr), + mErrors(this), + mImplementation(display->getImplementation() + ->createContext(mState, &mErrors, config, shareContext, attribs)), + mLabel(nullptr), + mCompiler(), + mConfig(config), + mHasBeenCurrent(false), + mContextLost(false), + mResetStatus(GraphicsResetStatus::NoError), + mContextLostForced(false), + mResetStrategy(GetResetStrategy(attribs)), + mSurfacelessSupported(displayExtensions.surfacelessContext), + mCurrentDrawSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)), + mCurrentReadSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)), + mDisplay(display), + mWebGLContext(GetWebGLContext(attribs)), + mBufferAccessValidationEnabled(false), + mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)), + mMemoryProgramCache(memoryProgramCache), + mMemoryShaderCache(memoryShaderCache), + mVertexArrayObserverBinding(this, kVertexArraySubjectIndex), + mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex), + mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex), + mProgramPipelineObserverBinding(this, kProgramPipelineSubjectIndex), + mSingleThreadPool(nullptr), + mMultiThreadPool(nullptr), + mFrameCapture(new angle::FrameCapture), + mRefCount(0), + mOverlay(mImplementation.get()), + mIsExternal(GetIsExternal(attribs)), + mSaveAndRestoreState(GetSaveAndRestoreState(attribs)), + mIsDestroyed(false) +{ + for (angle::SubjectIndex uboIndex = kUniformBuffer0SubjectIndex; + uboIndex < kUniformBufferMaxSubjectIndex; ++uboIndex) + { + mUniformBufferObserverBindings.emplace_back(this, uboIndex); + } + + for (angle::SubjectIndex acbIndex = kAtomicCounterBuffer0SubjectIndex; + acbIndex < kAtomicCounterBufferMaxSubjectIndex; ++acbIndex) + { + mAtomicCounterBufferObserverBindings.emplace_back(this, acbIndex); + } + + for (angle::SubjectIndex ssboIndex = kShaderStorageBuffer0SubjectIndex; + ssboIndex < kShaderStorageBufferMaxSubjectIndex; ++ssboIndex) + { + mShaderStorageBufferObserverBindings.emplace_back(this, ssboIndex); + } + + for (angle::SubjectIndex samplerIndex = kSampler0SubjectIndex; + samplerIndex < kSamplerMaxSubjectIndex; ++samplerIndex) + { + mSamplerObserverBindings.emplace_back(this, samplerIndex); + } + + for (angle::SubjectIndex imageIndex = kImage0SubjectIndex; imageIndex < kImageMaxSubjectIndex; + ++imageIndex) + { + mImageObserverBindings.emplace_back(this, imageIndex); + } + + // Implementations now require the display to be set at context creation. + ASSERT(mDisplay); +} + +egl::Error Context::initialize() +{ + if (!mImplementation) + return egl::Error(EGL_NOT_INITIALIZED, "native context creation failed"); + return egl::NoError(); +} + +void Context::initializeDefaultResources() +{ + mImplementation->setMemoryProgramCache(mMemoryProgramCache); + + initCaps(); + + mState.initialize(this); + + mDefaultFramebuffer = std::make_unique<Framebuffer>(this, mImplementation.get()); + + mFenceNVHandleAllocator.setBaseHandle(0); + + // [OpenGL ES 2.0.24] section 3.7 page 83: + // In the initial state, TEXTURE_2D and TEXTURE_CUBE_MAP have two-dimensional + // and cube map texture state vectors respectively associated with them. + // In order that access to these initial textures not be lost, they are treated as texture + // objects all of whose names are 0. + + Texture *zeroTexture2D = new Texture(mImplementation.get(), {0}, TextureType::_2D); + mZeroTextures[TextureType::_2D].set(this, zeroTexture2D); + + Texture *zeroTextureCube = new Texture(mImplementation.get(), {0}, TextureType::CubeMap); + mZeroTextures[TextureType::CubeMap].set(this, zeroTextureCube); + + if (getClientVersion() >= Version(3, 0) || mSupportedExtensions.texture3DOES) + { + Texture *zeroTexture3D = new Texture(mImplementation.get(), {0}, TextureType::_3D); + mZeroTextures[TextureType::_3D].set(this, zeroTexture3D); + } + if (getClientVersion() >= Version(3, 0)) + { + Texture *zeroTexture2DArray = + new Texture(mImplementation.get(), {0}, TextureType::_2DArray); + mZeroTextures[TextureType::_2DArray].set(this, zeroTexture2DArray); + } + if (getClientVersion() >= Version(3, 1) || mSupportedExtensions.textureMultisampleANGLE) + { + Texture *zeroTexture2DMultisample = + new Texture(mImplementation.get(), {0}, TextureType::_2DMultisample); + mZeroTextures[TextureType::_2DMultisample].set(this, zeroTexture2DMultisample); + } + if (getClientVersion() >= Version(3, 1)) + { + Texture *zeroTexture2DMultisampleArray = + new Texture(mImplementation.get(), {0}, TextureType::_2DMultisampleArray); + mZeroTextures[TextureType::_2DMultisampleArray].set(this, zeroTexture2DMultisampleArray); + + for (int i = 0; i < mState.mCaps.maxAtomicCounterBufferBindings; i++) + { + bindBufferRange(BufferBinding::AtomicCounter, i, {0}, 0, 0); + } + + for (int i = 0; i < mState.mCaps.maxShaderStorageBufferBindings; i++) + { + bindBufferRange(BufferBinding::ShaderStorage, i, {0}, 0, 0); + } + } + + if ((getClientType() != EGL_OPENGL_API && getClientVersion() >= Version(3, 2)) || + mSupportedExtensions.textureCubeMapArrayAny()) + { + Texture *zeroTextureCubeMapArray = + new Texture(mImplementation.get(), {0}, TextureType::CubeMapArray); + mZeroTextures[TextureType::CubeMapArray].set(this, zeroTextureCubeMapArray); + } + + if ((getClientType() != EGL_OPENGL_API && getClientVersion() >= Version(3, 2)) || + mSupportedExtensions.textureBufferAny()) + { + Texture *zeroTextureBuffer = new Texture(mImplementation.get(), {0}, TextureType::Buffer); + mZeroTextures[TextureType::Buffer].set(this, zeroTextureBuffer); + } + + if (mSupportedExtensions.textureRectangleANGLE) + { + Texture *zeroTextureRectangle = + new Texture(mImplementation.get(), {0}, TextureType::Rectangle); + mZeroTextures[TextureType::Rectangle].set(this, zeroTextureRectangle); + } + + if (mSupportedExtensions.EGLImageExternalOES || + mSupportedExtensions.EGLStreamConsumerExternalNV) + { + Texture *zeroTextureExternal = + new Texture(mImplementation.get(), {0}, TextureType::External); + mZeroTextures[TextureType::External].set(this, zeroTextureExternal); + } + + // This may change native TEXTURE_2D, TEXTURE_EXTERNAL_OES and TEXTURE_RECTANGLE, + // binding states. Ensure state manager is aware of this when binding + // this texture type. + if (mSupportedExtensions.videoTextureWEBGL) + { + Texture *zeroTextureVideoImage = + new Texture(mImplementation.get(), {0}, TextureType::VideoImage); + mZeroTextures[TextureType::VideoImage].set(this, zeroTextureVideoImage); + } + + mState.initializeZeroTextures(this, mZeroTextures); + + ANGLE_CONTEXT_TRY(mImplementation->initialize()); + + // Add context into the share group + mState.getShareGroup()->addSharedContext(this); + + bindVertexArray({0}); + + if (getClientVersion() >= Version(3, 0)) + { + // [OpenGL ES 3.0.2] section 2.14.1 pg 85: + // In the initial state, a default transform feedback object is bound and treated as + // a transform feedback object with a name of zero. That object is bound any time + // BindTransformFeedback is called with id of zero + bindTransformFeedback(GL_TRANSFORM_FEEDBACK, {0}); + } + + for (auto type : angle::AllEnums<BufferBinding>()) + { + bindBuffer(type, {0}); + } + + bindRenderbuffer(GL_RENDERBUFFER, {0}); + + for (int i = 0; i < mState.mCaps.maxUniformBufferBindings; i++) + { + bindBufferRange(BufferBinding::Uniform, i, {0}, 0, -1); + } + + // Initialize GLES1 renderer if appropriate. + if (getClientVersion() < Version(2, 0)) + { + mGLES1Renderer.reset(new GLES1Renderer()); + } + + // Initialize dirty bit masks + mAllDirtyBits.set(); + + mDrawDirtyObjects.set(State::DIRTY_OBJECT_ACTIVE_TEXTURES); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_DRAW_FRAMEBUFFER); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_VERTEX_ARRAY); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_PROGRAM); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_PROGRAM_PIPELINE_OBJECT); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_SAMPLERS); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_IMAGES); + + mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_STATE); + mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_BUFFER_BINDING); + mTexImageDirtyBits.set(State::DIRTY_BIT_EXTENDED); + // No dirty objects. + + // Readpixels uses the pack state and read FBO + mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_STATE); + mReadPixelsDirtyBits.set(State::DIRTY_BIT_PACK_BUFFER_BINDING); + mReadPixelsDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); + mReadPixelsDirtyObjects.set(State::DIRTY_OBJECT_READ_FRAMEBUFFER); + + mClearDirtyBits.set(State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED); + mClearDirtyBits.set(State::DIRTY_BIT_SCISSOR_TEST_ENABLED); + mClearDirtyBits.set(State::DIRTY_BIT_SCISSOR); + mClearDirtyBits.set(State::DIRTY_BIT_VIEWPORT); + mClearDirtyBits.set(State::DIRTY_BIT_CLEAR_COLOR); + mClearDirtyBits.set(State::DIRTY_BIT_CLEAR_DEPTH); + mClearDirtyBits.set(State::DIRTY_BIT_CLEAR_STENCIL); + mClearDirtyBits.set(State::DIRTY_BIT_COLOR_MASK); + mClearDirtyBits.set(State::DIRTY_BIT_DEPTH_MASK); + mClearDirtyBits.set(State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT); + mClearDirtyBits.set(State::DIRTY_BIT_STENCIL_WRITEMASK_BACK); + mClearDirtyBits.set(State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); + mClearDirtyObjects.set(State::DIRTY_OBJECT_DRAW_FRAMEBUFFER); + + // We sync the draw Framebuffer manually in prepareForClear to allow the clear calls to do + // more custom handling for robust resource init. + + mBlitDirtyBits.set(State::DIRTY_BIT_SCISSOR_TEST_ENABLED); + mBlitDirtyBits.set(State::DIRTY_BIT_SCISSOR); + mBlitDirtyBits.set(State::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE); + mBlitDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); + mBlitDirtyBits.set(State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); + mBlitDirtyObjects.set(State::DIRTY_OBJECT_READ_FRAMEBUFFER); + mBlitDirtyObjects.set(State::DIRTY_OBJECT_DRAW_FRAMEBUFFER); + + mComputeDirtyBits.set(State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING); + mComputeDirtyBits.set(State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS); + mComputeDirtyBits.set(State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING); + mComputeDirtyBits.set(State::DIRTY_BIT_PROGRAM_BINDING); + mComputeDirtyBits.set(State::DIRTY_BIT_PROGRAM_EXECUTABLE); + mComputeDirtyBits.set(State::DIRTY_BIT_TEXTURE_BINDINGS); + mComputeDirtyBits.set(State::DIRTY_BIT_SAMPLER_BINDINGS); + mComputeDirtyBits.set(State::DIRTY_BIT_IMAGE_BINDINGS); + mComputeDirtyBits.set(State::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_ACTIVE_TEXTURES); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_PROGRAM); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_PROGRAM_PIPELINE_OBJECT); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_IMAGES); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_SAMPLERS); + + mCopyImageDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); + mCopyImageDirtyObjects.set(State::DIRTY_OBJECT_READ_FRAMEBUFFER); + + mReadInvalidateDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); + mDrawInvalidateDirtyBits.set(State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); + + mOverlay.init(); +} + +egl::Error Context::onDestroy(const egl::Display *display) +{ + if (!mHasBeenCurrent) + { + // The context is never current, so default resources are not allocated. + return egl::NoError(); + } + + // eglDestoryContext() must have been called for this Context and there must not be any Threads + // that still have it current. + ASSERT(mIsDestroyed == true && mRefCount == 0); + + // Dump frame capture if enabled. + getShareGroup()->getFrameCaptureShared()->onDestroyContext(this); + + // Remove context from the capture share group + getShareGroup()->removeSharedContext(this); + + if (mGLES1Renderer) + { + mGLES1Renderer->onDestroy(this, &mState); + } + + ANGLE_TRY(unMakeCurrent(display)); + + mDefaultFramebuffer->onDestroy(this); + mDefaultFramebuffer.reset(); + + for (auto fence : mFenceNVMap) + { + if (fence.second) + { + fence.second->onDestroy(this); + } + SafeDelete(fence.second); + } + mFenceNVMap.clear(); + + for (auto query : mQueryMap) + { + if (query.second != nullptr) + { + query.second->release(this); + } + } + mQueryMap.clear(); + + for (auto vertexArray : mVertexArrayMap) + { + if (vertexArray.second) + { + vertexArray.second->onDestroy(this); + } + } + mVertexArrayMap.clear(); + + for (auto transformFeedback : mTransformFeedbackMap) + { + if (transformFeedback.second != nullptr) + { + transformFeedback.second->release(this); + } + } + mTransformFeedbackMap.clear(); + + for (BindingPointer<Texture> &zeroTexture : mZeroTextures) + { + if (zeroTexture.get() != nullptr) + { + zeroTexture.set(this, nullptr); + } + } + + releaseShaderCompiler(); + + mState.reset(this); + + mState.mBufferManager->release(this); + // mProgramPipelineManager must be before mShaderProgramManager to give each + // PPO the chance to release any references they have to the Programs that + // are bound to them before the Programs are released()'ed. + mState.mProgramPipelineManager->release(this); + mState.mShaderProgramManager->release(this); + mState.mTextureManager->release(this); + mState.mRenderbufferManager->release(this); + mState.mSamplerManager->release(this); + mState.mSyncManager->release(this); + mState.mFramebufferManager->release(this); + mState.mMemoryObjectManager->release(this); + mState.mSemaphoreManager->release(this); + + mSingleThreadPool.reset(); + mMultiThreadPool.reset(); + + mImplementation->onDestroy(this); + + // Backend requires implementation to be destroyed first to close down all the objects + mState.mShareGroup->release(display); + + mOverlay.destroy(this); + + return egl::NoError(); +} + +Context::~Context() {} + +void Context::setLabel(EGLLabelKHR label) +{ + mLabel = label; +} + +EGLLabelKHR Context::getLabel() const +{ + return mLabel; +} + +egl::Error Context::makeCurrent(egl::Display *display, + egl::Surface *drawSurface, + egl::Surface *readSurface) +{ + mDisplay = display; + + if (!mHasBeenCurrent) + { + initializeDefaultResources(); + initRendererString(); + initVersionStrings(); + initExtensionStrings(); + + int width = 0; + int height = 0; + if (drawSurface != nullptr) + { + width = drawSurface->getWidth(); + height = drawSurface->getHeight(); + } + + mState.setViewportParams(0, 0, width, height); + mState.setScissorParams(0, 0, width, height); + + mHasBeenCurrent = true; + } + + ANGLE_TRY(unsetDefaultFramebuffer()); + + getShareGroup()->getFrameCaptureShared()->onMakeCurrent(this, drawSurface); + + // TODO(jmadill): Rework this when we support ContextImpl + mState.setAllDirtyBits(); + mState.setAllDirtyObjects(); + + ANGLE_TRY(setDefaultFramebuffer(drawSurface, readSurface)); + + // Notify the renderer of a context switch. + angle::Result implResult = mImplementation->onMakeCurrent(this); + + // If the implementation fails onMakeCurrent, unset the default framebuffer. + if (implResult != angle::Result::Continue) + { + ANGLE_TRY(unsetDefaultFramebuffer()); + return angle::ResultToEGL(implResult); + } + + return egl::NoError(); +} + +egl::Error Context::unMakeCurrent(const egl::Display *display) +{ + ANGLE_TRY(angle::ResultToEGL(mImplementation->onUnMakeCurrent(this))); + + ANGLE_TRY(unsetDefaultFramebuffer()); + + // Return the scratch buffers to the display so they can be shared with other contexts while + // this one is not current. + if (mScratchBuffer.valid()) + { + mDisplay->returnScratchBuffer(mScratchBuffer.release()); + } + if (mZeroFilledBuffer.valid()) + { + mDisplay->returnZeroFilledBuffer(mZeroFilledBuffer.release()); + } + + return egl::NoError(); +} + +BufferID Context::createBuffer() +{ + return mState.mBufferManager->createBuffer(); +} + +GLuint Context::createProgram() +{ + return mState.mShaderProgramManager->createProgram(mImplementation.get()).value; +} + +GLuint Context::createShader(ShaderType type) +{ + return mState.mShaderProgramManager + ->createShader(mImplementation.get(), mState.mLimitations, type) + .value; +} + +TextureID Context::createTexture() +{ + return mState.mTextureManager->createTexture(); +} + +RenderbufferID Context::createRenderbuffer() +{ + return mState.mRenderbufferManager->createRenderbuffer(); +} + +// Returns an unused framebuffer name +FramebufferID Context::createFramebuffer() +{ + return mState.mFramebufferManager->createFramebuffer(); +} + +void Context::genFencesNV(GLsizei n, FenceNVID *fences) +{ + for (int i = 0; i < n; i++) + { + GLuint handle = mFenceNVHandleAllocator.allocate(); + mFenceNVMap.assign({handle}, new FenceNV(mImplementation.get())); + fences[i] = {handle}; + } +} + +ProgramPipelineID Context::createProgramPipeline() +{ + return mState.mProgramPipelineManager->createProgramPipeline(); +} + +GLuint Context::createShaderProgramv(ShaderType type, GLsizei count, const GLchar *const *strings) +{ + const ShaderProgramID shaderID = PackParam<ShaderProgramID>(createShader(type)); + if (shaderID.value) + { + Shader *shaderObject = getShader(shaderID); + ASSERT(shaderObject); + shaderObject->setSource(count, strings, nullptr); + shaderObject->compile(this); + const ShaderProgramID programID = PackParam<ShaderProgramID>(createProgram()); + if (programID.value) + { + gl::Program *programObject = getProgramNoResolveLink(programID); + ASSERT(programObject); + + if (shaderObject->isCompiled(this)) + { + // As per Khronos issue 2261: + // https://gitlab.khronos.org/Tracker/vk-gl-cts/issues/2261 + // We must wait to mark the program separable until it's successfully compiled. + programObject->setSeparable(true); + + programObject->attachShader(shaderObject); + + if (programObject->link(this) != angle::Result::Continue) + { + deleteShader(shaderID); + deleteProgram(programID); + return 0u; + } + if (onProgramLink(programObject) != angle::Result::Continue) + { + deleteShader(shaderID); + deleteProgram(programID); + return 0u; + } + + programObject->detachShader(this, shaderObject); + } + + InfoLog &programInfoLog = programObject->getExecutable().getInfoLog(); + programInfoLog << shaderObject->getInfoLogString(); + } + + deleteShader(shaderID); + + return programID.value; + } + + return 0u; +} + +MemoryObjectID Context::createMemoryObject() +{ + return mState.mMemoryObjectManager->createMemoryObject(mImplementation.get()); +} + +SemaphoreID Context::createSemaphore() +{ + return mState.mSemaphoreManager->createSemaphore(mImplementation.get()); +} + +void Context::deleteBuffer(BufferID bufferName) +{ + Buffer *buffer = mState.mBufferManager->getBuffer(bufferName); + if (buffer) + { + detachBuffer(buffer); + } + + mState.mBufferManager->deleteObject(this, bufferName); +} + +void Context::deleteShader(ShaderProgramID shader) +{ + mState.mShaderProgramManager->deleteShader(this, shader); +} + +void Context::deleteProgram(ShaderProgramID program) +{ + mState.mShaderProgramManager->deleteProgram(this, program); +} + +void Context::deleteTexture(TextureID texture) +{ + if (mState.mTextureManager->getTexture(texture)) + { + detachTexture(texture); + } + + mState.mTextureManager->deleteObject(this, texture); +} + +void Context::deleteRenderbuffer(RenderbufferID renderbuffer) +{ + if (mState.mRenderbufferManager->getRenderbuffer(renderbuffer)) + { + detachRenderbuffer(renderbuffer); + } + + mState.mRenderbufferManager->deleteObject(this, renderbuffer); +} + +void Context::deleteSync(GLsync sync) +{ + // The spec specifies the underlying Fence object is not deleted until all current + // wait commands finish. However, since the name becomes invalid, we cannot query the fence, + // and since our API is currently designed for being called from a single thread, we can delete + // the fence immediately. + mState.mSyncManager->deleteObject(this, static_cast<GLuint>(reinterpret_cast<uintptr_t>(sync))); +} + +void Context::deleteProgramPipeline(ProgramPipelineID pipelineID) +{ + ProgramPipeline *pipeline = mState.mProgramPipelineManager->getProgramPipeline(pipelineID); + if (pipeline) + { + detachProgramPipeline(pipelineID); + } + + mState.mProgramPipelineManager->deleteObject(this, pipelineID); +} + +void Context::deleteMemoryObject(MemoryObjectID memoryObject) +{ + mState.mMemoryObjectManager->deleteMemoryObject(this, memoryObject); +} + +void Context::deleteSemaphore(SemaphoreID semaphore) +{ + mState.mSemaphoreManager->deleteSemaphore(this, semaphore); +} + +// GL_CHROMIUM_lose_context +void Context::loseContext(GraphicsResetStatus current, GraphicsResetStatus other) +{ + // TODO(geofflang): mark the rest of the share group lost. Requires access to the entire share + // group from a context. http://anglebug.com/3379 + markContextLost(current); +} + +void Context::deleteFramebuffer(FramebufferID framebufferID) +{ + // We are responsible for deleting the GL objects from the Framebuffer's pixel local storage. + std::unique_ptr<PixelLocalStorage> plsToDelete; + + Framebuffer *framebuffer = mState.mFramebufferManager->getFramebuffer(framebufferID); + if (framebuffer) + { + plsToDelete = framebuffer->detachPixelLocalStorage(); + detachFramebuffer(framebufferID); + } + + mState.mFramebufferManager->deleteObject(this, framebufferID); + + // Delete the pixel local storage GL objects after the framebuffer, in order to avoid any + // potential trickyness with orphaning. + if (plsToDelete) + { + plsToDelete->deleteContextObjects(this); + } +} + +void Context::deleteFencesNV(GLsizei n, const FenceNVID *fences) +{ + for (int i = 0; i < n; i++) + { + FenceNVID fence = fences[i]; + + FenceNV *fenceObject = nullptr; + if (mFenceNVMap.erase(fence, &fenceObject)) + { + mFenceNVHandleAllocator.release(fence.value); + if (fenceObject) + { + fenceObject->onDestroy(this); + } + delete fenceObject; + } + } +} + +Buffer *Context::getBuffer(BufferID handle) const +{ + return mState.mBufferManager->getBuffer(handle); +} + +Renderbuffer *Context::getRenderbuffer(RenderbufferID handle) const +{ + return mState.mRenderbufferManager->getRenderbuffer(handle); +} + +EGLenum Context::getContextPriority() const +{ + return egl::ToEGLenum(mImplementation->getContextPriority()); +} + +Sync *Context::getSync(GLsync handle) const +{ + return mState.mSyncManager->getSync(static_cast<GLuint>(reinterpret_cast<uintptr_t>(handle))); +} + +VertexArray *Context::getVertexArray(VertexArrayID handle) const +{ + return mVertexArrayMap.query(handle); +} + +Sampler *Context::getSampler(SamplerID handle) const +{ + return mState.mSamplerManager->getSampler(handle); +} + +TransformFeedback *Context::getTransformFeedback(TransformFeedbackID handle) const +{ + return mTransformFeedbackMap.query(handle); +} + +ProgramPipeline *Context::getProgramPipeline(ProgramPipelineID handle) const +{ + return mState.mProgramPipelineManager->getProgramPipeline(handle); +} + +gl::LabeledObject *Context::getLabeledObject(GLenum identifier, GLuint name) const +{ + switch (identifier) + { + case GL_BUFFER: + case GL_BUFFER_OBJECT_EXT: + return getBuffer({name}); + case GL_SHADER: + case GL_SHADER_OBJECT_EXT: + return getShader({name}); + case GL_PROGRAM: + case GL_PROGRAM_OBJECT_EXT: + return getProgramNoResolveLink({name}); + case GL_VERTEX_ARRAY: + case GL_VERTEX_ARRAY_OBJECT_EXT: + return getVertexArray({name}); + case GL_QUERY: + case GL_QUERY_OBJECT_EXT: + return getQuery({name}); + case GL_TRANSFORM_FEEDBACK: + return getTransformFeedback({name}); + case GL_SAMPLER: + return getSampler({name}); + case GL_TEXTURE: + return getTexture({name}); + case GL_RENDERBUFFER: + return getRenderbuffer({name}); + case GL_FRAMEBUFFER: + return getFramebuffer({name}); + case GL_PROGRAM_PIPELINE: + case GL_PROGRAM_PIPELINE_OBJECT_EXT: + return getProgramPipeline({name}); + default: + UNREACHABLE(); + return nullptr; + } +} + +gl::LabeledObject *Context::getLabeledObjectFromPtr(const void *ptr) const +{ + return getSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))); +} + +void Context::objectLabel(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) +{ + gl::LabeledObject *object = getLabeledObject(identifier, name); + ASSERT(object != nullptr); + + std::string labelName = GetObjectLabelFromPointer(length, label); + ANGLE_CONTEXT_TRY(object->setLabel(this, labelName)); + + // TODO(jmadill): Determine if the object is dirty based on 'name'. Conservatively assume the + // specified object is active until we do this. + mState.setObjectDirty(identifier); +} + +void Context::labelObject(GLenum type, GLuint object, GLsizei length, const GLchar *label) +{ + gl::LabeledObject *obj = getLabeledObject(type, object); + ASSERT(obj != nullptr); + + std::string labelName = ""; + if (label != nullptr) + { + size_t labelLength = length == 0 ? strlen(label) : length; + labelName = std::string(label, labelLength); + } + ANGLE_CONTEXT_TRY(obj->setLabel(this, labelName)); + mState.setObjectDirty(type); +} + +void Context::objectPtrLabel(const void *ptr, GLsizei length, const GLchar *label) +{ + gl::LabeledObject *object = getLabeledObjectFromPtr(ptr); + ASSERT(object != nullptr); + + std::string labelName = GetObjectLabelFromPointer(length, label); + ANGLE_CONTEXT_TRY(object->setLabel(this, labelName)); +} + +void Context::getObjectLabel(GLenum identifier, + GLuint name, + GLsizei bufSize, + GLsizei *length, + GLchar *label) +{ + gl::LabeledObject *object = getLabeledObject(identifier, name); + ASSERT(object != nullptr); + + const std::string &objectLabel = object->getLabel(); + GetObjectLabelBase(objectLabel, bufSize, length, label); +} + +void Context::getObjectPtrLabel(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) +{ + gl::LabeledObject *object = getLabeledObjectFromPtr(ptr); + ASSERT(object != nullptr); + + const std::string &objectLabel = object->getLabel(); + GetObjectLabelBase(objectLabel, bufSize, length, label); +} + +GLboolean Context::isSampler(SamplerID samplerName) const +{ + return mState.mSamplerManager->isSampler(samplerName); +} + +void Context::bindTexture(TextureType target, TextureID handle) +{ + // Some apps enable KHR_create_context_no_error but pass in an invalid texture type. + // Workaround this by silently returning in such situations. + if (target == TextureType::InvalidEnum) + { + return; + } + + Texture *texture = nullptr; + if (handle.value == 0) + { + texture = mZeroTextures[target].get(); + } + else + { + texture = + mState.mTextureManager->checkTextureAllocation(mImplementation.get(), handle, target); + } + + ASSERT(texture); + // Early return if rebinding the same texture + if (texture == mState.getTargetTexture(target)) + { + return; + } + + mState.setSamplerTexture(this, target, texture); + mStateCache.onActiveTextureChange(this); +} + +void Context::bindReadFramebuffer(FramebufferID framebufferHandle) +{ + Framebuffer *framebuffer = mState.mFramebufferManager->checkFramebufferAllocation( + mImplementation.get(), this, framebufferHandle); + mState.setReadFramebufferBinding(framebuffer); + mReadFramebufferObserverBinding.bind(framebuffer); +} + +void Context::bindDrawFramebuffer(FramebufferID framebufferHandle) +{ + Framebuffer *framebuffer = mState.mFramebufferManager->checkFramebufferAllocation( + mImplementation.get(), this, framebufferHandle); + mState.setDrawFramebufferBinding(framebuffer); + mDrawFramebufferObserverBinding.bind(framebuffer); + mStateCache.onDrawFramebufferChange(this); +} + +void Context::bindVertexArray(VertexArrayID vertexArrayHandle) +{ + VertexArray *vertexArray = checkVertexArrayAllocation(vertexArrayHandle); + mState.setVertexArrayBinding(this, vertexArray); + mVertexArrayObserverBinding.bind(vertexArray); + mStateCache.onVertexArrayBindingChange(this); +} + +void Context::bindVertexBuffer(GLuint bindingIndex, + BufferID bufferHandle, + GLintptr offset, + GLsizei stride) +{ + Buffer *buffer = + mState.mBufferManager->checkBufferAllocation(mImplementation.get(), bufferHandle); + mState.bindVertexBuffer(this, bindingIndex, buffer, offset, stride); + mStateCache.onVertexArrayStateChange(this); +} + +void Context::bindSampler(GLuint textureUnit, SamplerID samplerHandle) +{ + ASSERT(textureUnit < static_cast<GLuint>(mState.mCaps.maxCombinedTextureImageUnits)); + Sampler *sampler = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), samplerHandle); + + // Early return if rebinding the same sampler + if (sampler == mState.getSampler(textureUnit)) + { + return; + } + + mState.setSamplerBinding(this, textureUnit, sampler); + mSamplerObserverBindings[textureUnit].bind(sampler); + mStateCache.onActiveTextureChange(this); +} + +void Context::bindImageTexture(GLuint unit, + TextureID texture, + GLint level, + GLboolean layered, + GLint layer, + GLenum access, + GLenum format) +{ + Texture *tex = mState.mTextureManager->getTexture(texture); + mState.setImageUnit(this, unit, tex, level, layered, layer, access, format); + mImageObserverBindings[unit].bind(tex); +} + +void Context::useProgram(ShaderProgramID program) +{ + ANGLE_CONTEXT_TRY(mState.setProgram(this, getProgramResolveLink(program))); + mStateCache.onProgramExecutableChange(this); +} + +void Context::useProgramStages(ProgramPipelineID pipeline, + GLbitfield stages, + ShaderProgramID program) +{ + Program *shaderProgram = getProgramNoResolveLink(program); + ProgramPipeline *programPipeline = + mState.mProgramPipelineManager->checkProgramPipelineAllocation(mImplementation.get(), + pipeline); + + ASSERT(programPipeline); + ANGLE_CONTEXT_TRY(programPipeline->useProgramStages(this, stages, shaderProgram)); +} + +void Context::bindTransformFeedback(GLenum target, TransformFeedbackID transformFeedbackHandle) +{ + ASSERT(target == GL_TRANSFORM_FEEDBACK); + TransformFeedback *transformFeedback = + checkTransformFeedbackAllocation(transformFeedbackHandle); + mState.setTransformFeedbackBinding(this, transformFeedback); + mStateCache.onActiveTransformFeedbackChange(this); +} + +void Context::bindProgramPipeline(ProgramPipelineID pipelineHandle) +{ + ProgramPipeline *pipeline = mState.mProgramPipelineManager->checkProgramPipelineAllocation( + mImplementation.get(), pipelineHandle); + ANGLE_CONTEXT_TRY(mState.setProgramPipelineBinding(this, pipeline)); + mStateCache.onProgramExecutableChange(this); + mProgramPipelineObserverBinding.bind(pipeline); +} + +void Context::beginQuery(QueryType target, QueryID query) +{ + Query *queryObject = getOrCreateQuery(query, target); + ASSERT(queryObject); + + // begin query + ANGLE_CONTEXT_TRY(queryObject->begin(this)); + + // set query as active for specified target only if begin succeeded + mState.setActiveQuery(this, target, queryObject); + mStateCache.onQueryChange(this); +} + +void Context::endQuery(QueryType target) +{ + Query *queryObject = mState.getActiveQuery(target); + ASSERT(queryObject); + + // Intentionally don't call try here. We don't want an early return. + (void)(queryObject->end(this)); + + // Always unbind the query, even if there was an error. This may delete the query object. + mState.setActiveQuery(this, target, nullptr); + mStateCache.onQueryChange(this); +} + +void Context::queryCounter(QueryID id, QueryType target) +{ + ASSERT(target == QueryType::Timestamp); + + Query *queryObject = getOrCreateQuery(id, target); + ASSERT(queryObject); + + ANGLE_CONTEXT_TRY(queryObject->queryCounter(this)); +} + +void Context::getQueryiv(QueryType target, GLenum pname, GLint *params) +{ + switch (pname) + { + case GL_CURRENT_QUERY_EXT: + params[0] = mState.getActiveQueryId(target).value; + break; + case GL_QUERY_COUNTER_BITS_EXT: + switch (target) + { + case QueryType::TimeElapsed: + params[0] = getCaps().queryCounterBitsTimeElapsed; + break; + case QueryType::Timestamp: + params[0] = getCaps().queryCounterBitsTimestamp; + break; + default: + UNREACHABLE(); + params[0] = 0; + break; + } + break; + default: + UNREACHABLE(); + return; + } +} + +void Context::getQueryivRobust(QueryType target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getQueryiv(target, pname, params); +} + +void Context::getUnsignedBytev(GLenum pname, GLubyte *data) +{ + UNIMPLEMENTED(); +} + +void Context::getUnsignedBytei_v(GLenum target, GLuint index, GLubyte *data) +{ + UNIMPLEMENTED(); +} + +void Context::getQueryObjectiv(QueryID id, GLenum pname, GLint *params) +{ + ANGLE_CONTEXT_TRY(GetQueryObjectParameter(this, getQuery(id), pname, params)); +} + +void Context::getQueryObjectivRobust(QueryID id, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getQueryObjectiv(id, pname, params); +} + +void Context::getQueryObjectuiv(QueryID id, GLenum pname, GLuint *params) +{ + ANGLE_CONTEXT_TRY(GetQueryObjectParameter(this, getQuery(id), pname, params)); +} + +void Context::getQueryObjectuivRobust(QueryID id, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + getQueryObjectuiv(id, pname, params); +} + +void Context::getQueryObjecti64v(QueryID id, GLenum pname, GLint64 *params) +{ + ANGLE_CONTEXT_TRY(GetQueryObjectParameter(this, getQuery(id), pname, params)); +} + +void Context::getQueryObjecti64vRobust(QueryID id, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint64 *params) +{ + getQueryObjecti64v(id, pname, params); +} + +void Context::getQueryObjectui64v(QueryID id, GLenum pname, GLuint64 *params) +{ + ANGLE_CONTEXT_TRY(GetQueryObjectParameter(this, getQuery(id), pname, params)); +} + +void Context::getQueryObjectui64vRobust(QueryID id, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLuint64 *params) +{ + getQueryObjectui64v(id, pname, params); +} + +Framebuffer *Context::getFramebuffer(FramebufferID handle) const +{ + return mState.mFramebufferManager->getFramebuffer(handle); +} + +FenceNV *Context::getFenceNV(FenceNVID handle) const +{ + return mFenceNVMap.query(handle); +} + +Query *Context::getOrCreateQuery(QueryID handle, QueryType type) +{ + if (!mQueryMap.contains(handle)) + { + return nullptr; + } + + Query *query = mQueryMap.query(handle); + if (!query) + { + ASSERT(type != QueryType::InvalidEnum); + query = new Query(mImplementation.get(), type, handle); + query->addRef(); + mQueryMap.assign(handle, query); + } + return query; +} + +Query *Context::getQuery(QueryID handle) const +{ + return mQueryMap.query(handle); +} + +Texture *Context::getTextureByType(TextureType type) const +{ + ASSERT(ValidTextureTarget(this, type) || ValidTextureExternalTarget(this, type)); + return mState.getTargetTexture(type); +} + +Texture *Context::getTextureByTarget(TextureTarget target) const +{ + return getTextureByType(TextureTargetToType(target)); +} + +Texture *Context::getSamplerTexture(unsigned int sampler, TextureType type) const +{ + return mState.getSamplerTexture(sampler, type); +} + +Compiler *Context::getCompiler() const +{ + if (mCompiler.get() == nullptr) + { + mCompiler.set(this, new Compiler(mImplementation.get(), mState, mDisplay)); + } + return mCompiler.get(); +} + +void Context::getBooleanvImpl(GLenum pname, GLboolean *params) const +{ + switch (pname) + { + case GL_SHADER_COMPILER: + *params = GL_TRUE; + break; + case GL_CONTEXT_ROBUST_ACCESS_EXT: + *params = ConvertToGLBoolean(mState.hasRobustAccess()); + break; + + default: + mState.getBooleanv(pname, params); + break; + } +} + +void Context::getFloatvImpl(GLenum pname, GLfloat *params) const +{ + // Queries about context capabilities and maximums are answered by Context. + // Queries about current GL state values are answered by State. + switch (pname) + { + case GL_ALIASED_LINE_WIDTH_RANGE: + params[0] = mState.mCaps.minAliasedLineWidth; + params[1] = mState.mCaps.maxAliasedLineWidth; + break; + case GL_ALIASED_POINT_SIZE_RANGE: + params[0] = mState.mCaps.minAliasedPointSize; + params[1] = mState.mCaps.maxAliasedPointSize; + break; + case GL_SMOOTH_POINT_SIZE_RANGE: + params[0] = mState.mCaps.minSmoothPointSize; + params[1] = mState.mCaps.maxSmoothPointSize; + break; + case GL_SMOOTH_LINE_WIDTH_RANGE: + params[0] = mState.mCaps.minSmoothLineWidth; + params[1] = mState.mCaps.maxSmoothLineWidth; + break; + case GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: + ASSERT(mState.mExtensions.textureFilterAnisotropicEXT); + *params = mState.mCaps.maxTextureAnisotropy; + break; + case GL_MAX_TEXTURE_LOD_BIAS: + *params = mState.mCaps.maxLODBias; + break; + case GL_MIN_FRAGMENT_INTERPOLATION_OFFSET: + *params = mState.mCaps.minInterpolationOffset; + break; + case GL_MAX_FRAGMENT_INTERPOLATION_OFFSET: + *params = mState.mCaps.maxInterpolationOffset; + break; + case GL_PRIMITIVE_BOUNDING_BOX: + params[0] = mState.mBoundingBoxMinX; + params[1] = mState.mBoundingBoxMinY; + params[2] = mState.mBoundingBoxMinZ; + params[3] = mState.mBoundingBoxMinW; + params[4] = mState.mBoundingBoxMaxX; + params[5] = mState.mBoundingBoxMaxY; + params[6] = mState.mBoundingBoxMaxZ; + params[7] = mState.mBoundingBoxMaxW; + break; + default: + mState.getFloatv(pname, params); + break; + } +} + +void Context::getIntegervImpl(GLenum pname, GLint *params) const +{ + // Queries about context capabilities and maximums are answered by Context. + // Queries about current GL state values are answered by State. + + switch (pname) + { + case GL_MAX_VERTEX_ATTRIBS: + *params = mState.mCaps.maxVertexAttributes; + break; + case GL_MAX_VERTEX_UNIFORM_VECTORS: + *params = mState.mCaps.maxVertexUniformVectors; + break; + case GL_MAX_VERTEX_UNIFORM_COMPONENTS: + *params = mState.mCaps.maxShaderUniformComponents[ShaderType::Vertex]; + break; + case GL_MAX_VARYING_VECTORS: + *params = mState.mCaps.maxVaryingVectors; + break; + case GL_MAX_VARYING_COMPONENTS: + *params = mState.mCaps.maxVaryingVectors * 4; + break; + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + *params = mState.mCaps.maxCombinedTextureImageUnits; + break; + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + *params = mState.mCaps.maxShaderTextureImageUnits[ShaderType::Vertex]; + break; + case GL_MAX_TEXTURE_IMAGE_UNITS: + *params = mState.mCaps.maxShaderTextureImageUnits[ShaderType::Fragment]; + break; + case GL_MAX_FRAGMENT_UNIFORM_VECTORS: + *params = mState.mCaps.maxFragmentUniformVectors; + break; + case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: + *params = mState.mCaps.maxShaderUniformComponents[ShaderType::Fragment]; + break; + case GL_MAX_RENDERBUFFER_SIZE: + *params = mState.mCaps.maxRenderbufferSize; + break; + case GL_MAX_COLOR_ATTACHMENTS_EXT: + *params = mState.mCaps.maxColorAttachments; + break; + case GL_MAX_DRAW_BUFFERS_EXT: + *params = mState.mCaps.maxDrawBuffers; + break; + case GL_SUBPIXEL_BITS: + *params = mState.mCaps.subPixelBits; + break; + case GL_MAX_TEXTURE_SIZE: + *params = mState.mCaps.max2DTextureSize; + break; + case GL_MAX_RECTANGLE_TEXTURE_SIZE_ANGLE: + *params = mState.mCaps.maxRectangleTextureSize; + break; + case GL_MAX_CUBE_MAP_TEXTURE_SIZE: + *params = mState.mCaps.maxCubeMapTextureSize; + break; + case GL_MAX_3D_TEXTURE_SIZE: + *params = mState.mCaps.max3DTextureSize; + break; + case GL_MAX_ARRAY_TEXTURE_LAYERS: + *params = mState.mCaps.maxArrayTextureLayers; + break; + case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: + *params = mState.mCaps.uniformBufferOffsetAlignment; + break; + case GL_MAX_UNIFORM_BUFFER_BINDINGS: + *params = mState.mCaps.maxUniformBufferBindings; + break; + case GL_MAX_VERTEX_UNIFORM_BLOCKS: + *params = mState.mCaps.maxShaderUniformBlocks[ShaderType::Vertex]; + break; + case GL_MAX_FRAGMENT_UNIFORM_BLOCKS: + *params = mState.mCaps.maxShaderUniformBlocks[ShaderType::Fragment]; + break; + case GL_MAX_COMBINED_UNIFORM_BLOCKS: + *params = mState.mCaps.maxCombinedUniformBlocks; + break; + case GL_MAX_VERTEX_OUTPUT_COMPONENTS: + *params = mState.mCaps.maxVertexOutputComponents; + break; + case GL_MAX_FRAGMENT_INPUT_COMPONENTS: + *params = mState.mCaps.maxFragmentInputComponents; + break; + case GL_MIN_PROGRAM_TEXEL_OFFSET: + *params = mState.mCaps.minProgramTexelOffset; + break; + case GL_MAX_PROGRAM_TEXEL_OFFSET: + *params = mState.mCaps.maxProgramTexelOffset; + break; + case GL_MAJOR_VERSION: + *params = getClientVersion().major; + break; + case GL_MINOR_VERSION: + *params = getClientVersion().minor; + break; + case GL_MAX_ELEMENTS_INDICES: + *params = mState.mCaps.maxElementsIndices; + break; + case GL_MAX_ELEMENTS_VERTICES: + *params = mState.mCaps.maxElementsVertices; + break; + case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: + *params = mState.mCaps.maxTransformFeedbackInterleavedComponents; + break; + case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: + *params = mState.mCaps.maxTransformFeedbackSeparateAttributes; + break; + case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: + *params = mState.mCaps.maxTransformFeedbackSeparateComponents; + break; + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: + *params = static_cast<GLint>(mState.mCaps.compressedTextureFormats.size()); + break; + case GL_MAX_SAMPLES_ANGLE: + *params = mState.mCaps.maxSamples; + break; + case GL_MAX_VIEWPORT_DIMS: + { + params[0] = mState.mCaps.maxViewportWidth; + params[1] = mState.mCaps.maxViewportHeight; + } + break; + case GL_COMPRESSED_TEXTURE_FORMATS: + std::copy(mState.mCaps.compressedTextureFormats.begin(), + mState.mCaps.compressedTextureFormats.end(), params); + break; + case GL_RESET_NOTIFICATION_STRATEGY_EXT: + *params = mResetStrategy; + break; + case GL_NUM_SHADER_BINARY_FORMATS: + *params = static_cast<GLint>(mState.mCaps.shaderBinaryFormats.size()); + break; + case GL_SHADER_BINARY_FORMATS: + std::copy(mState.mCaps.shaderBinaryFormats.begin(), + mState.mCaps.shaderBinaryFormats.end(), params); + break; + case GL_NUM_PROGRAM_BINARY_FORMATS: + *params = static_cast<GLint>(mState.mCaps.programBinaryFormats.size()); + break; + case GL_PROGRAM_BINARY_FORMATS: + std::copy(mState.mCaps.programBinaryFormats.begin(), + mState.mCaps.programBinaryFormats.end(), params); + break; + case GL_NUM_EXTENSIONS: + *params = static_cast<GLint>(mExtensionStrings.size()); + break; + + // Desktop client flags + case GL_CONTEXT_FLAGS: + { + GLint contextFlags = 0; + if (mState.hasProtectedContent()) + { + contextFlags |= GL_CONTEXT_FLAG_PROTECTED_CONTENT_BIT_EXT; + } + + if (mState.isDebugContext()) + { + contextFlags |= GL_CONTEXT_FLAG_DEBUG_BIT_KHR; + } + + if (mState.hasRobustAccess()) + { + contextFlags |= GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT; + } + *params = contextFlags; + } + break; + case GL_CONTEXT_PROFILE_MASK: + ASSERT(getClientType() == EGL_OPENGL_API); + *params = mState.getProfileMask(); + break; + + // GL_ANGLE_request_extension + case GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE: + *params = static_cast<GLint>(mRequestableExtensionStrings.size()); + break; + + // GL_KHR_debug + case GL_MAX_DEBUG_MESSAGE_LENGTH: + *params = mState.mCaps.maxDebugMessageLength; + break; + case GL_MAX_DEBUG_LOGGED_MESSAGES: + *params = mState.mCaps.maxDebugLoggedMessages; + break; + case GL_MAX_DEBUG_GROUP_STACK_DEPTH: + *params = mState.mCaps.maxDebugGroupStackDepth; + break; + case GL_MAX_LABEL_LENGTH: + *params = mState.mCaps.maxLabelLength; + break; + + // GL_OVR_multiview2 + case GL_MAX_VIEWS_OVR: + *params = mState.mCaps.maxViews; + break; + + // GL_EXT_disjoint_timer_query + case GL_GPU_DISJOINT_EXT: + *params = mImplementation->getGPUDisjoint(); + break; + case GL_MAX_FRAMEBUFFER_WIDTH: + *params = mState.mCaps.maxFramebufferWidth; + break; + case GL_MAX_FRAMEBUFFER_HEIGHT: + *params = mState.mCaps.maxFramebufferHeight; + break; + case GL_MAX_FRAMEBUFFER_SAMPLES: + *params = mState.mCaps.maxFramebufferSamples; + break; + case GL_MAX_SAMPLE_MASK_WORDS: + *params = mState.mCaps.maxSampleMaskWords; + break; + case GL_MAX_COLOR_TEXTURE_SAMPLES: + *params = mState.mCaps.maxColorTextureSamples; + break; + case GL_MAX_DEPTH_TEXTURE_SAMPLES: + *params = mState.mCaps.maxDepthTextureSamples; + break; + case GL_MAX_INTEGER_SAMPLES: + *params = mState.mCaps.maxIntegerSamples; + break; + case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET: + *params = mState.mCaps.maxVertexAttribRelativeOffset; + break; + case GL_MAX_VERTEX_ATTRIB_BINDINGS: + *params = mState.mCaps.maxVertexAttribBindings; + break; + case GL_MAX_VERTEX_ATTRIB_STRIDE: + *params = mState.mCaps.maxVertexAttribStride; + break; + case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS: + *params = mState.mCaps.maxShaderAtomicCounterBuffers[ShaderType::Vertex]; + break; + case GL_MAX_VERTEX_ATOMIC_COUNTERS: + *params = mState.mCaps.maxShaderAtomicCounters[ShaderType::Vertex]; + break; + case GL_MAX_VERTEX_IMAGE_UNIFORMS: + *params = mState.mCaps.maxShaderImageUniforms[ShaderType::Vertex]; + break; + case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: + *params = mState.mCaps.maxShaderStorageBlocks[ShaderType::Vertex]; + break; + case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS: + *params = mState.mCaps.maxShaderAtomicCounterBuffers[ShaderType::Fragment]; + break; + case GL_MAX_FRAGMENT_ATOMIC_COUNTERS: + *params = mState.mCaps.maxShaderAtomicCounters[ShaderType::Fragment]; + break; + case GL_MAX_FRAGMENT_IMAGE_UNIFORMS: + *params = mState.mCaps.maxShaderImageUniforms[ShaderType::Fragment]; + break; + case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS: + *params = mState.mCaps.maxShaderStorageBlocks[ShaderType::Fragment]; + break; + case GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET: + *params = mState.mCaps.minProgramTextureGatherOffset; + break; + case GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET: + *params = mState.mCaps.maxProgramTextureGatherOffset; + break; + case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: + *params = mState.mCaps.maxComputeWorkGroupInvocations; + break; + case GL_MAX_COMPUTE_UNIFORM_BLOCKS: + *params = mState.mCaps.maxShaderUniformBlocks[ShaderType::Compute]; + break; + case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS: + *params = mState.mCaps.maxShaderTextureImageUnits[ShaderType::Compute]; + break; + case GL_MAX_COMPUTE_SHARED_MEMORY_SIZE: + *params = mState.mCaps.maxComputeSharedMemorySize; + break; + case GL_MAX_COMPUTE_UNIFORM_COMPONENTS: + *params = mState.mCaps.maxShaderUniformComponents[ShaderType::Compute]; + break; + case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS: + *params = mState.mCaps.maxShaderAtomicCounterBuffers[ShaderType::Compute]; + break; + case GL_MAX_COMPUTE_ATOMIC_COUNTERS: + *params = mState.mCaps.maxShaderAtomicCounters[ShaderType::Compute]; + break; + case GL_MAX_COMPUTE_IMAGE_UNIFORMS: + *params = mState.mCaps.maxShaderImageUniforms[ShaderType::Compute]; + break; + case GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS: + *params = static_cast<GLint>( + mState.mCaps.maxCombinedShaderUniformComponents[ShaderType::Compute]); + break; + case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: + *params = mState.mCaps.maxShaderStorageBlocks[ShaderType::Compute]; + break; + case GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES: + *params = mState.mCaps.maxCombinedShaderOutputResources; + break; + case GL_MAX_UNIFORM_LOCATIONS: + *params = mState.mCaps.maxUniformLocations; + break; + case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS: + *params = mState.mCaps.maxAtomicCounterBufferBindings; + break; + case GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE: + *params = mState.mCaps.maxAtomicCounterBufferSize; + break; + case GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS: + *params = mState.mCaps.maxCombinedAtomicCounterBuffers; + break; + case GL_MAX_COMBINED_ATOMIC_COUNTERS: + *params = mState.mCaps.maxCombinedAtomicCounters; + break; + case GL_MAX_IMAGE_UNITS: + *params = mState.mCaps.maxImageUnits; + break; + case GL_MAX_COMBINED_IMAGE_UNIFORMS: + *params = mState.mCaps.maxCombinedImageUniforms; + break; + case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: + *params = mState.mCaps.maxShaderStorageBufferBindings; + break; + case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: + *params = mState.mCaps.maxCombinedShaderStorageBlocks; + break; + case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT: + *params = mState.mCaps.shaderStorageBufferOffsetAlignment; + break; + + // GL_EXT_geometry_shader + case GL_MAX_FRAMEBUFFER_LAYERS_EXT: + *params = mState.mCaps.maxFramebufferLayers; + break; + case GL_LAYER_PROVOKING_VERTEX_EXT: + *params = mState.mCaps.layerProvokingVertex; + break; + case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT: + *params = mState.mCaps.maxShaderUniformComponents[ShaderType::Geometry]; + break; + case GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT: + *params = mState.mCaps.maxShaderUniformBlocks[ShaderType::Geometry]; + break; + case GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT: + *params = static_cast<GLint>( + mState.mCaps.maxCombinedShaderUniformComponents[ShaderType::Geometry]); + break; + case GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT: + *params = mState.mCaps.maxGeometryInputComponents; + break; + case GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT: + *params = mState.mCaps.maxGeometryOutputComponents; + break; + case GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT: + *params = mState.mCaps.maxGeometryOutputVertices; + break; + case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT: + *params = mState.mCaps.maxGeometryTotalOutputComponents; + break; + case GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT: + *params = mState.mCaps.maxGeometryShaderInvocations; + break; + case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT: + *params = mState.mCaps.maxShaderTextureImageUnits[ShaderType::Geometry]; + break; + case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT: + *params = mState.mCaps.maxShaderAtomicCounterBuffers[ShaderType::Geometry]; + break; + case GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT: + *params = mState.mCaps.maxShaderAtomicCounters[ShaderType::Geometry]; + break; + case GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT: + *params = mState.mCaps.maxShaderImageUniforms[ShaderType::Geometry]; + break; + case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT: + *params = mState.mCaps.maxShaderStorageBlocks[ShaderType::Geometry]; + break; + // GL_EXT_tessellation_shader + case GL_MAX_PATCH_VERTICES_EXT: + *params = mState.mCaps.maxPatchVertices; + break; + case GL_MAX_TESS_GEN_LEVEL_EXT: + *params = mState.mCaps.maxTessGenLevel; + break; + case GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_EXT: + *params = mState.mCaps.maxShaderUniformComponents[ShaderType::TessControl]; + break; + case GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT: + *params = mState.mCaps.maxShaderUniformComponents[ShaderType::TessEvaluation]; + break; + case GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_EXT: + *params = mState.mCaps.maxShaderTextureImageUnits[ShaderType::TessControl]; + break; + case GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_EXT: + *params = mState.mCaps.maxShaderTextureImageUnits[ShaderType::TessEvaluation]; + break; + case GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT: + *params = mState.mCaps.maxTessControlOutputComponents; + break; + case GL_MAX_TESS_PATCH_COMPONENTS_EXT: + *params = mState.mCaps.maxTessPatchComponents; + break; + case GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_EXT: + *params = mState.mCaps.maxTessControlTotalOutputComponents; + break; + case GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT: + *params = mState.mCaps.maxTessEvaluationOutputComponents; + break; + case GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_EXT: + *params = mState.mCaps.maxShaderUniformBlocks[ShaderType::TessControl]; + break; + case GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_EXT: + *params = mState.mCaps.maxShaderUniformBlocks[ShaderType::TessEvaluation]; + break; + case GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT: + *params = mState.mCaps.maxTessControlInputComponents; + break; + case GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT: + *params = mState.mCaps.maxTessEvaluationInputComponents; + break; + case GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT: + *params = static_cast<GLint>( + mState.mCaps.maxCombinedShaderUniformComponents[ShaderType::TessControl]); + break; + case GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT: + *params = static_cast<GLint>( + mState.mCaps.maxCombinedShaderUniformComponents[ShaderType::TessEvaluation]); + break; + case GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT: + *params = mState.mCaps.maxShaderAtomicCounterBuffers[ShaderType::TessControl]; + break; + case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT: + *params = mState.mCaps.maxShaderAtomicCounterBuffers[ShaderType::TessEvaluation]; + break; + case GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT: + *params = mState.mCaps.maxShaderAtomicCounters[ShaderType::TessControl]; + break; + case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT: + *params = mState.mCaps.maxShaderAtomicCounters[ShaderType::TessEvaluation]; + break; + case GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_EXT: + *params = mState.mCaps.maxShaderImageUniforms[ShaderType::TessControl]; + break; + case GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_EXT: + *params = mState.mCaps.maxShaderImageUniforms[ShaderType::TessEvaluation]; + break; + case GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT: + *params = mState.mCaps.maxShaderStorageBlocks[ShaderType::TessControl]; + break; + case GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT: + *params = mState.mCaps.maxShaderStorageBlocks[ShaderType::TessEvaluation]; + break; + // GLES1 emulation: Caps queries + case GL_MAX_TEXTURE_UNITS: + *params = mState.mCaps.maxMultitextureUnits; + break; + case GL_MAX_MODELVIEW_STACK_DEPTH: + *params = mState.mCaps.maxModelviewMatrixStackDepth; + break; + case GL_MAX_PROJECTION_STACK_DEPTH: + *params = mState.mCaps.maxProjectionMatrixStackDepth; + break; + case GL_MAX_TEXTURE_STACK_DEPTH: + *params = mState.mCaps.maxTextureMatrixStackDepth; + break; + case GL_MAX_LIGHTS: + *params = mState.mCaps.maxLights; + break; + + // case GL_MAX_CLIP_DISTANCES_EXT: Conflict enum value + case GL_MAX_CLIP_PLANES: + if (getClientVersion().major >= 2) + { + // GL_APPLE_clip_distance/GL_EXT_clip_cull_distance + *params = mState.mCaps.maxClipDistances; + } + else + { + *params = mState.mCaps.maxClipPlanes; + } + break; + case GL_MAX_CULL_DISTANCES_EXT: + *params = mState.mCaps.maxCullDistances; + break; + case GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT: + *params = mState.mCaps.maxCombinedClipAndCullDistances; + break; + // GLES1 emulation: Vertex attribute queries + case GL_VERTEX_ARRAY_BUFFER_BINDING: + case GL_NORMAL_ARRAY_BUFFER_BINDING: + case GL_COLOR_ARRAY_BUFFER_BINDING: + case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES: + case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: + getIntegerVertexAttribImpl(pname, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, params); + break; + case GL_VERTEX_ARRAY_STRIDE: + case GL_NORMAL_ARRAY_STRIDE: + case GL_COLOR_ARRAY_STRIDE: + case GL_POINT_SIZE_ARRAY_STRIDE_OES: + case GL_TEXTURE_COORD_ARRAY_STRIDE: + getIntegerVertexAttribImpl(pname, GL_VERTEX_ATTRIB_ARRAY_STRIDE, params); + break; + case GL_VERTEX_ARRAY_SIZE: + case GL_COLOR_ARRAY_SIZE: + case GL_TEXTURE_COORD_ARRAY_SIZE: + getIntegerVertexAttribImpl(pname, GL_VERTEX_ATTRIB_ARRAY_SIZE, params); + break; + case GL_VERTEX_ARRAY_TYPE: + case GL_COLOR_ARRAY_TYPE: + case GL_NORMAL_ARRAY_TYPE: + case GL_POINT_SIZE_ARRAY_TYPE_OES: + case GL_TEXTURE_COORD_ARRAY_TYPE: + getIntegerVertexAttribImpl(pname, GL_VERTEX_ATTRIB_ARRAY_TYPE, params); + break; + + // GL_KHR_parallel_shader_compile + case GL_MAX_SHADER_COMPILER_THREADS_KHR: + *params = mState.getMaxShaderCompilerThreads(); + break; + + // GL_EXT_blend_func_extended + case GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT: + *params = mState.mCaps.maxDualSourceDrawBuffers; + break; + + // OES_shader_multisample_interpolation + case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES: + *params = mState.mCaps.subPixelInterpolationOffsetBits; + break; + + // GL_OES_texture_buffer + case GL_MAX_TEXTURE_BUFFER_SIZE: + *params = mState.mCaps.maxTextureBufferSize; + break; + case GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT: + *params = mState.mCaps.textureBufferOffsetAlignment; + break; + + // GL_EXT_clip_control + case GL_CLIP_ORIGIN_EXT: + *params = mState.mClipControlOrigin; + break; + case GL_CLIP_DEPTH_MODE_EXT: + *params = mState.mClipControlDepth; + break; + + // ANGLE_shader_pixel_local_storage + case GL_MAX_PIXEL_LOCAL_STORAGE_PLANES_ANGLE: + *params = mState.mCaps.maxPixelLocalStoragePlanes; + break; + case GL_MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE_ANGLE: + *params = mState.mCaps.maxColorAttachmentsWithActivePixelLocalStorage; + break; + case GL_MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES_ANGLE: + *params = mState.mCaps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes; + break; + + default: + ANGLE_CONTEXT_TRY(mState.getIntegerv(this, pname, params)); + break; + } +} + +void Context::getIntegerVertexAttribImpl(GLenum pname, GLenum attribpname, GLint *params) const +{ + getVertexAttribivImpl(static_cast<GLuint>(vertexArrayIndex(ParamToVertexArrayType(pname))), + attribpname, params); +} + +void Context::getInteger64vImpl(GLenum pname, GLint64 *params) const +{ + // Queries about context capabilities and maximums are answered by Context. + // Queries about current GL state values are answered by State. + switch (pname) + { + case GL_MAX_ELEMENT_INDEX: + *params = mState.mCaps.maxElementIndex; + break; + case GL_MAX_UNIFORM_BLOCK_SIZE: + *params = mState.mCaps.maxUniformBlockSize; + break; + case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: + *params = mState.mCaps.maxCombinedShaderUniformComponents[ShaderType::Vertex]; + break; + case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: + *params = mState.mCaps.maxCombinedShaderUniformComponents[ShaderType::Fragment]; + break; + case GL_MAX_SERVER_WAIT_TIMEOUT: + *params = mState.mCaps.maxServerWaitTimeout; + break; + + // GL_EXT_disjoint_timer_query + case GL_TIMESTAMP_EXT: + *params = mImplementation->getTimestamp(); + break; + + case GL_MAX_SHADER_STORAGE_BLOCK_SIZE: + *params = mState.mCaps.maxShaderStorageBlockSize; + break; + default: + UNREACHABLE(); + break; + } +} + +void Context::getPointerv(GLenum pname, void **params) +{ + mState.getPointerv(this, pname, params); +} + +void Context::getPointervRobustANGLERobust(GLenum pname, + GLsizei bufSize, + GLsizei *length, + void **params) +{ + UNIMPLEMENTED(); +} + +void Context::getIntegeri_v(GLenum target, GLuint index, GLint *data) +{ + // Queries about context capabilities and maximums are answered by Context. + // Queries about current GL state values are answered by State. + + GLenum nativeType; + unsigned int numParams; + bool queryStatus = getIndexedQueryParameterInfo(target, &nativeType, &numParams); + ASSERT(queryStatus); + + if (nativeType == GL_INT) + { + switch (target) + { + case GL_MAX_COMPUTE_WORK_GROUP_COUNT: + ASSERT(index < 3u); + *data = mState.mCaps.maxComputeWorkGroupCount[index]; + break; + case GL_MAX_COMPUTE_WORK_GROUP_SIZE: + ASSERT(index < 3u); + *data = mState.mCaps.maxComputeWorkGroupSize[index]; + break; + default: + mState.getIntegeri_v(this, target, index, data); + } + } + else + { + CastIndexedStateValues(this, nativeType, target, index, numParams, data); + } +} + +void Context::getIntegeri_vRobust(GLenum target, + GLuint index, + GLsizei bufSize, + GLsizei *length, + GLint *data) +{ + getIntegeri_v(target, index, data); +} + +void Context::getInteger64i_v(GLenum target, GLuint index, GLint64 *data) +{ + // Queries about context capabilities and maximums are answered by Context. + // Queries about current GL state values are answered by State. + + GLenum nativeType; + unsigned int numParams; + bool queryStatus = getIndexedQueryParameterInfo(target, &nativeType, &numParams); + ASSERT(queryStatus); + + if (nativeType == GL_INT_64_ANGLEX) + { + mState.getInteger64i_v(target, index, data); + } + else + { + CastIndexedStateValues(this, nativeType, target, index, numParams, data); + } +} + +void Context::getInteger64i_vRobust(GLenum target, + GLuint index, + GLsizei bufSize, + GLsizei *length, + GLint64 *data) +{ + getInteger64i_v(target, index, data); +} + +void Context::getBooleani_v(GLenum target, GLuint index, GLboolean *data) +{ + // Queries about context capabilities and maximums are answered by Context. + // Queries about current GL state values are answered by State. + + GLenum nativeType; + unsigned int numParams; + bool queryStatus = getIndexedQueryParameterInfo(target, &nativeType, &numParams); + ASSERT(queryStatus); + + if (nativeType == GL_BOOL) + { + mState.getBooleani_v(target, index, data); + } + else + { + CastIndexedStateValues(this, nativeType, target, index, numParams, data); + } +} + +void Context::getBooleani_vRobust(GLenum target, + GLuint index, + GLsizei bufSize, + GLsizei *length, + GLboolean *data) +{ + getBooleani_v(target, index, data); +} + +void Context::getBufferParameteriv(BufferBinding target, GLenum pname, GLint *params) +{ + Buffer *buffer = mState.getTargetBuffer(target); + QueryBufferParameteriv(buffer, pname, params); +} + +void Context::getBufferParameterivRobust(BufferBinding target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getBufferParameteriv(target, pname, params); +} + +void Context::getFramebufferAttachmentParameteriv(GLenum target, + GLenum attachment, + GLenum pname, + GLint *params) +{ + const Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + QueryFramebufferAttachmentParameteriv(this, framebuffer, attachment, pname, params); +} + +void Context::getFramebufferAttachmentParameterivRobust(GLenum target, + GLenum attachment, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getFramebufferAttachmentParameteriv(target, attachment, pname, params); +} + +void Context::getRenderbufferParameteriv(GLenum target, GLenum pname, GLint *params) +{ + Renderbuffer *renderbuffer = mState.getCurrentRenderbuffer(); + QueryRenderbufferiv(this, renderbuffer, pname, params); +} + +void Context::getRenderbufferParameterivRobust(GLenum target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getRenderbufferParameteriv(target, pname, params); +} + +void Context::texBuffer(TextureType target, GLenum internalformat, BufferID buffer) +{ + ASSERT(target == TextureType::Buffer); + + Texture *texture = getTextureByType(target); + Buffer *bufferObj = mState.mBufferManager->getBuffer(buffer); + ANGLE_CONTEXT_TRY(texture->setBuffer(this, bufferObj, internalformat)); +} + +void Context::texBufferRange(TextureType target, + GLenum internalformat, + BufferID buffer, + GLintptr offset, + GLsizeiptr size) +{ + ASSERT(target == TextureType::Buffer); + + Texture *texture = getTextureByType(target); + Buffer *bufferObj = mState.mBufferManager->getBuffer(buffer); + ANGLE_CONTEXT_TRY(texture->setBufferRange(this, bufferObj, internalformat, offset, size)); +} + +void Context::getTexParameterfv(TextureType target, GLenum pname, GLfloat *params) +{ + const Texture *const texture = getTextureByType(target); + QueryTexParameterfv(this, texture, pname, params); +} + +void Context::getTexParameterfvRobust(TextureType target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + getTexParameterfv(target, pname, params); +} + +void Context::getTexParameteriv(TextureType target, GLenum pname, GLint *params) +{ + const Texture *const texture = getTextureByType(target); + QueryTexParameteriv(this, texture, pname, params); +} + +void Context::getTexParameterIiv(TextureType target, GLenum pname, GLint *params) +{ + const Texture *const texture = getTextureByType(target); + QueryTexParameterIiv(this, texture, pname, params); +} + +void Context::getTexParameterIuiv(TextureType target, GLenum pname, GLuint *params) +{ + const Texture *const texture = getTextureByType(target); + QueryTexParameterIuiv(this, texture, pname, params); +} + +void Context::getTexParameterivRobust(TextureType target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getTexParameteriv(target, pname, params); +} + +void Context::getTexParameterIivRobust(TextureType target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getTexParameterIuivRobust(TextureType target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getTexLevelParameteriv(TextureTarget target, GLint level, GLenum pname, GLint *params) +{ + Texture *texture = getTextureByTarget(target); + QueryTexLevelParameteriv(texture, target, level, pname, params); +} + +void Context::getTexLevelParameterivRobust(TextureTarget target, + GLint level, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getTexLevelParameterfv(TextureTarget target, + GLint level, + GLenum pname, + GLfloat *params) +{ + Texture *texture = getTextureByTarget(target); + QueryTexLevelParameterfv(texture, target, level, pname, params); +} + +void Context::getTexLevelParameterfvRobust(TextureTarget target, + GLint level, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + UNIMPLEMENTED(); +} + +void Context::texParameterf(TextureType target, GLenum pname, GLfloat param) +{ + Texture *const texture = getTextureByType(target); + SetTexParameterf(this, texture, pname, param); +} + +void Context::texParameterfv(TextureType target, GLenum pname, const GLfloat *params) +{ + Texture *const texture = getTextureByType(target); + SetTexParameterfv(this, texture, pname, params); +} + +void Context::texParameterfvRobust(TextureType target, + GLenum pname, + GLsizei bufSize, + const GLfloat *params) +{ + texParameterfv(target, pname, params); +} + +void Context::texParameteri(TextureType target, GLenum pname, GLint param) +{ + // Some apps enable KHR_create_context_no_error but pass in an invalid texture type. + // Workaround this by silently returning in such situations. + if (target == TextureType::InvalidEnum) + { + return; + } + + Texture *const texture = getTextureByType(target); + SetTexParameteri(this, texture, pname, param); +} + +void Context::texParameteriv(TextureType target, GLenum pname, const GLint *params) +{ + Texture *const texture = getTextureByType(target); + SetTexParameteriv(this, texture, pname, params); +} + +void Context::texParameterIiv(TextureType target, GLenum pname, const GLint *params) +{ + Texture *const texture = getTextureByType(target); + SetTexParameterIiv(this, texture, pname, params); +} + +void Context::texParameterIuiv(TextureType target, GLenum pname, const GLuint *params) +{ + Texture *const texture = getTextureByType(target); + SetTexParameterIuiv(this, texture, pname, params); +} + +void Context::texParameterivRobust(TextureType target, + GLenum pname, + GLsizei bufSize, + const GLint *params) +{ + texParameteriv(target, pname, params); +} + +void Context::texParameterIivRobust(TextureType target, + GLenum pname, + GLsizei bufSize, + const GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::texParameterIuivRobust(TextureType target, + GLenum pname, + GLsizei bufSize, + const GLuint *params) +{ + UNIMPLEMENTED(); +} + +void Context::drawArraysInstanced(PrimitiveMode mode, + GLint first, + GLsizei count, + GLsizei instanceCount) +{ + // No-op if count draws no primitives for given mode + if (noopDrawInstanced(mode, count, instanceCount)) + { + ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->drawArraysInstanced(this, mode, first, count, instanceCount)); + MarkTransformFeedbackBufferUsage(this, count, instanceCount); + MarkShaderStorageUsage(this); +} + +void Context::drawElementsInstanced(PrimitiveMode mode, + GLsizei count, + DrawElementsType type, + const void *indices, + GLsizei instances) +{ + // No-op if count draws no primitives for given mode + if (noopDrawInstanced(mode, count, instances)) + { + ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->drawElementsInstanced(this, mode, count, type, indices, instances)); + MarkShaderStorageUsage(this); +} + +void Context::drawElementsBaseVertex(PrimitiveMode mode, + GLsizei count, + DrawElementsType type, + const void *indices, + GLint basevertex) +{ + // No-op if count draws no primitives for given mode + if (noopDraw(mode, count)) + { + ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->drawElementsBaseVertex(this, mode, count, type, indices, basevertex)); + MarkShaderStorageUsage(this); +} + +void Context::drawElementsInstancedBaseVertex(PrimitiveMode mode, + GLsizei count, + DrawElementsType type, + const void *indices, + GLsizei instancecount, + GLint basevertex) +{ + // No-op if count draws no primitives for given mode + if (noopDrawInstanced(mode, count, instancecount)) + { + ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->drawElementsInstancedBaseVertex( + this, mode, count, type, indices, instancecount, basevertex)); + MarkShaderStorageUsage(this); +} + +void Context::drawRangeElements(PrimitiveMode mode, + GLuint start, + GLuint end, + GLsizei count, + DrawElementsType type, + const void *indices) +{ + // No-op if count draws no primitives for given mode + if (noopDraw(mode, count)) + { + ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->drawRangeElements(this, mode, start, end, count, type, indices)); + MarkShaderStorageUsage(this); +} + +void Context::drawRangeElementsBaseVertex(PrimitiveMode mode, + GLuint start, + GLuint end, + GLsizei count, + DrawElementsType type, + const void *indices, + GLint basevertex) +{ + // No-op if count draws no primitives for given mode + if (noopDraw(mode, count)) + { + ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->drawRangeElementsBaseVertex(this, mode, start, end, count, + type, indices, basevertex)); + MarkShaderStorageUsage(this); +} + +void Context::drawArraysIndirect(PrimitiveMode mode, const void *indirect) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->drawArraysIndirect(this, mode, indirect)); + MarkShaderStorageUsage(this); +} + +void Context::drawElementsIndirect(PrimitiveMode mode, DrawElementsType type, const void *indirect) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->drawElementsIndirect(this, mode, type, indirect)); + MarkShaderStorageUsage(this); +} + +void Context::flush() +{ + ANGLE_CONTEXT_TRY(mImplementation->flush(this)); +} + +void Context::finish() +{ + ANGLE_CONTEXT_TRY(mImplementation->finish(this)); +} + +void Context::insertEventMarker(GLsizei length, const char *marker) +{ + ASSERT(mImplementation); + ANGLE_CONTEXT_TRY(mImplementation->insertEventMarker(length, marker)); +} + +void Context::pushGroupMarker(GLsizei length, const char *marker) +{ + ASSERT(mImplementation); + + if (marker == nullptr) + { + // From the EXT_debug_marker spec, + // "If <marker> is null then an empty string is pushed on the stack." + ANGLE_CONTEXT_TRY(mImplementation->pushGroupMarker(length, "")); + } + else + { + ANGLE_CONTEXT_TRY(mImplementation->pushGroupMarker(length, marker)); + } +} + +void Context::popGroupMarker() +{ + ASSERT(mImplementation); + ANGLE_CONTEXT_TRY(mImplementation->popGroupMarker()); +} + +void Context::bindUniformLocation(ShaderProgramID program, + UniformLocation location, + const GLchar *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + + programObject->bindUniformLocation(location, name); +} + +void Context::coverageModulation(GLenum components) +{ + mState.setCoverageModulation(components); +} + +GLuint Context::getProgramResourceIndex(ShaderProgramID program, + GLenum programInterface, + const GLchar *name) +{ + const Program *programObject = getProgramResolveLink(program); + return QueryProgramResourceIndex(programObject, programInterface, name); +} + +void Context::getProgramResourceName(ShaderProgramID program, + GLenum programInterface, + GLuint index, + GLsizei bufSize, + GLsizei *length, + GLchar *name) +{ + const Program *programObject = getProgramResolveLink(program); + QueryProgramResourceName(this, programObject, programInterface, index, bufSize, length, name); +} + +GLint Context::getProgramResourceLocation(ShaderProgramID program, + GLenum programInterface, + const GLchar *name) +{ + const Program *programObject = getProgramResolveLink(program); + return QueryProgramResourceLocation(programObject, programInterface, name); +} + +void Context::getProgramResourceiv(ShaderProgramID program, + GLenum programInterface, + GLuint index, + GLsizei propCount, + const GLenum *props, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + const Program *programObject = getProgramResolveLink(program); + QueryProgramResourceiv(programObject, programInterface, {index}, propCount, props, bufSize, + length, params); +} + +void Context::getProgramInterfaceiv(ShaderProgramID program, + GLenum programInterface, + GLenum pname, + GLint *params) +{ + const Program *programObject = getProgramResolveLink(program); + QueryProgramInterfaceiv(programObject, programInterface, pname, params); +} + +void Context::getProgramInterfaceivRobust(ShaderProgramID program, + GLenum programInterface, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::handleError(GLenum errorCode, + const char *message, + const char *file, + const char *function, + unsigned int line) +{ + mErrors.handleError(errorCode, message, file, function, line); +} + +void Context::validationError(angle::EntryPoint entryPoint, + GLenum errorCode, + const char *message) const +{ + const_cast<Context *>(this)->mErrors.validationError(entryPoint, errorCode, message); +} + +void Context::validationErrorF(angle::EntryPoint entryPoint, + GLenum errorCode, + const char *format, + ...) const +{ + va_list vargs; + va_start(vargs, format); + constexpr size_t kMessageSize = 256; + char message[kMessageSize]; + int r = vsnprintf(message, kMessageSize, format, vargs); + va_end(vargs); + + if (r > 0) + { + validationError(entryPoint, errorCode, message); + } + else + { + validationError(entryPoint, errorCode, format); + } +} + +// Get one of the recorded errors and clear its flag, if any. +// [OpenGL ES 2.0.24] section 2.5 page 13. +GLenum Context::getError() +{ + if (mErrors.empty()) + { + return GL_NO_ERROR; + } + else + { + return mErrors.popError(); + } +} + +// NOTE: this function should not assume that this context is current! +void Context::markContextLost(GraphicsResetStatus status) +{ + ASSERT(status != GraphicsResetStatus::NoError); + if (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT) + { + mResetStatus = status; + mContextLostForced = true; + } + setContextLost(); +} + +void Context::setContextLost() +{ + mContextLost = true; + + // Stop skipping validation, since many implementation entrypoint assume they can't + // be called when lost, or with null object arguments, etc. + mSkipValidation = false; + + // Make sure we update TLS. +#if defined(ANGLE_PLATFORM_APPLE) + SetCurrentValidContextTLS(nullptr); +#else + gCurrentValidContext = nullptr; +#endif +} + +GLenum Context::getGraphicsResetStatus() +{ + // Even if the application doesn't want to know about resets, we want to know + // as it will allow us to skip all the calls. + if (mResetStrategy == GL_NO_RESET_NOTIFICATION_EXT) + { + if (!isContextLost() && mImplementation->getResetStatus() != GraphicsResetStatus::NoError) + { + setContextLost(); + } + + // EXT_robustness, section 2.6: If the reset notification behavior is + // NO_RESET_NOTIFICATION_EXT, then the implementation will never deliver notification of + // reset events, and GetGraphicsResetStatusEXT will always return NO_ERROR. + return GL_NO_ERROR; + } + + // The GL_EXT_robustness spec says that if a reset is encountered, a reset + // status should be returned at least once, and GL_NO_ERROR should be returned + // once the device has finished resetting. + if (!isContextLost()) + { + ASSERT(mResetStatus == GraphicsResetStatus::NoError); + mResetStatus = mImplementation->getResetStatus(); + + if (mResetStatus != GraphicsResetStatus::NoError) + { + setContextLost(); + } + } + else if (!mContextLostForced && mResetStatus != GraphicsResetStatus::NoError) + { + // If markContextLost was used to mark the context lost then + // assume that is not recoverable, and continue to report the + // lost reset status for the lifetime of this context. + mResetStatus = mImplementation->getResetStatus(); + } + + return ToGLenum(mResetStatus); +} + +bool Context::isResetNotificationEnabled() const +{ + return (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT); +} + +bool Context::isRobustnessEnabled() const +{ + return mState.hasRobustAccess(); +} + +const egl::Config *Context::getConfig() const +{ + return mConfig; +} + +EGLenum Context::getClientType() const +{ + return mState.getClientType(); +} + +EGLenum Context::getRenderBuffer() const +{ + const Framebuffer *framebuffer = + mState.mFramebufferManager->getFramebuffer(Framebuffer::kDefaultDrawFramebufferHandle); + if (framebuffer == nullptr) + { + return EGL_NONE; + } + + const FramebufferAttachment *backAttachment = framebuffer->getAttachment(this, GL_BACK); + ASSERT(backAttachment != nullptr); + return backAttachment->getSurface()->getRenderBuffer(); +} + +VertexArray *Context::checkVertexArrayAllocation(VertexArrayID vertexArrayHandle) +{ + // Only called after a prior call to Gen. + VertexArray *vertexArray = getVertexArray(vertexArrayHandle); + if (!vertexArray) + { + vertexArray = + new VertexArray(mImplementation.get(), vertexArrayHandle, + mState.mCaps.maxVertexAttributes, mState.mCaps.maxVertexAttribBindings); + vertexArray->setBufferAccessValidationEnabled(mBufferAccessValidationEnabled); + + mVertexArrayMap.assign(vertexArrayHandle, vertexArray); + } + + return vertexArray; +} + +TransformFeedback *Context::checkTransformFeedbackAllocation( + TransformFeedbackID transformFeedbackHandle) +{ + // Only called after a prior call to Gen. + TransformFeedback *transformFeedback = getTransformFeedback(transformFeedbackHandle); + if (!transformFeedback) + { + transformFeedback = + new TransformFeedback(mImplementation.get(), transformFeedbackHandle, mState.mCaps); + transformFeedback->addRef(); + mTransformFeedbackMap.assign(transformFeedbackHandle, transformFeedback); + } + + return transformFeedback; +} + +bool Context::isVertexArrayGenerated(VertexArrayID vertexArray) const +{ + ASSERT(mVertexArrayMap.contains({0})); + return mVertexArrayMap.contains(vertexArray); +} + +bool Context::isTransformFeedbackGenerated(TransformFeedbackID transformFeedback) const +{ + ASSERT(mTransformFeedbackMap.contains({0})); + return mTransformFeedbackMap.contains(transformFeedback); +} + +void Context::detachTexture(TextureID texture) +{ + // The State cannot unbind image observers itself, they are owned by the Context + Texture *tex = mState.mTextureManager->getTexture(texture); + for (auto &imageBinding : mImageObserverBindings) + { + if (imageBinding.getSubject() == tex) + { + imageBinding.reset(); + } + } + + // Simple pass-through to State's detachTexture method, as textures do not require + // allocation map management either here or in the resource manager at detach time. + // Zero textures are held by the Context, and we don't attempt to request them from + // the State. + mState.detachTexture(this, mZeroTextures, texture); +} + +void Context::detachBuffer(Buffer *buffer) +{ + // Simple pass-through to State's detachBuffer method, since + // only buffer attachments to container objects that are bound to the current context + // should be detached. And all those are available in State. + + // [OpenGL ES 3.2] section 5.1.2 page 45: + // Attachments to unbound container objects, such as + // deletion of a buffer attached to a vertex array object which is not bound to the context, + // are not affected and continue to act as references on the deleted object + ANGLE_CONTEXT_TRY(mState.detachBuffer(this, buffer)); +} + +void Context::detachFramebuffer(FramebufferID framebuffer) +{ + // Framebuffer detachment is handled by Context, because 0 is a valid + // Framebuffer object, and a pointer to it must be passed from Context + // to State at binding time. + + // [OpenGL ES 2.0.24] section 4.4 page 107: + // If a framebuffer that is currently bound to the target FRAMEBUFFER is deleted, it is as + // though BindFramebuffer had been executed with the target of FRAMEBUFFER and framebuffer of + // zero. + + if (mState.removeReadFramebufferBinding(framebuffer) && framebuffer.value != 0) + { + bindReadFramebuffer({0}); + } + + if (mState.removeDrawFramebufferBinding(framebuffer) && framebuffer.value != 0) + { + bindDrawFramebuffer({0}); + } +} + +void Context::detachRenderbuffer(RenderbufferID renderbuffer) +{ + mState.detachRenderbuffer(this, renderbuffer); +} + +void Context::detachVertexArray(VertexArrayID vertexArray) +{ + // Vertex array detachment is handled by Context, because 0 is a valid + // VAO, and a pointer to it must be passed from Context to State at + // binding time. + + // [OpenGL ES 3.0.2] section 2.10 page 43: + // If a vertex array object that is currently bound is deleted, the binding + // for that object reverts to zero and the default vertex array becomes current. + if (mState.removeVertexArrayBinding(this, vertexArray)) + { + bindVertexArray({0}); + } +} + +void Context::detachTransformFeedback(TransformFeedbackID transformFeedback) +{ + // Transform feedback detachment is handled by Context, because 0 is a valid + // transform feedback, and a pointer to it must be passed from Context to State at + // binding time. + + // The OpenGL specification doesn't mention what should happen when the currently bound + // transform feedback object is deleted. Since it is a container object, we treat it like + // VAOs and FBOs and set the current bound transform feedback back to 0. + if (mState.removeTransformFeedbackBinding(this, transformFeedback)) + { + bindTransformFeedback(GL_TRANSFORM_FEEDBACK, {0}); + mStateCache.onActiveTransformFeedbackChange(this); + } +} + +void Context::detachSampler(SamplerID sampler) +{ + mState.detachSampler(this, sampler); +} + +void Context::detachProgramPipeline(ProgramPipelineID pipeline) +{ + mState.detachProgramPipeline(this, pipeline); +} + +void Context::vertexAttribDivisor(GLuint index, GLuint divisor) +{ + mState.setVertexAttribDivisor(this, index, divisor); + mStateCache.onVertexArrayStateChange(this); +} + +void Context::samplerParameteri(SamplerID sampler, GLenum pname, GLint param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameteri(this, samplerObject, pname, param); +} + +void Context::samplerParameteriv(SamplerID sampler, GLenum pname, const GLint *param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameteriv(this, samplerObject, pname, param); +} + +void Context::samplerParameterIiv(SamplerID sampler, GLenum pname, const GLint *param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameterIiv(this, samplerObject, pname, param); +} + +void Context::samplerParameterIuiv(SamplerID sampler, GLenum pname, const GLuint *param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameterIuiv(this, samplerObject, pname, param); +} + +void Context::samplerParameterivRobust(SamplerID sampler, + GLenum pname, + GLsizei bufSize, + const GLint *param) +{ + samplerParameteriv(sampler, pname, param); +} + +void Context::samplerParameterIivRobust(SamplerID sampler, + GLenum pname, + GLsizei bufSize, + const GLint *param) +{ + UNIMPLEMENTED(); +} + +void Context::samplerParameterIuivRobust(SamplerID sampler, + GLenum pname, + GLsizei bufSize, + const GLuint *param) +{ + UNIMPLEMENTED(); +} + +void Context::samplerParameterf(SamplerID sampler, GLenum pname, GLfloat param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameterf(this, samplerObject, pname, param); +} + +void Context::samplerParameterfv(SamplerID sampler, GLenum pname, const GLfloat *param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameterfv(this, samplerObject, pname, param); +} + +void Context::samplerParameterfvRobust(SamplerID sampler, + GLenum pname, + GLsizei bufSize, + const GLfloat *param) +{ + samplerParameterfv(sampler, pname, param); +} + +void Context::getSamplerParameteriv(SamplerID sampler, GLenum pname, GLint *params) +{ + const Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + QuerySamplerParameteriv(samplerObject, pname, params); +} + +void Context::getSamplerParameterIiv(SamplerID sampler, GLenum pname, GLint *params) +{ + const Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + QuerySamplerParameterIiv(samplerObject, pname, params); +} + +void Context::getSamplerParameterIuiv(SamplerID sampler, GLenum pname, GLuint *params) +{ + const Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + QuerySamplerParameterIuiv(samplerObject, pname, params); +} + +void Context::getSamplerParameterivRobust(SamplerID sampler, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getSamplerParameteriv(sampler, pname, params); +} + +void Context::getSamplerParameterIivRobust(SamplerID sampler, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getSamplerParameterIuivRobust(SamplerID sampler, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getSamplerParameterfv(SamplerID sampler, GLenum pname, GLfloat *params) +{ + const Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + QuerySamplerParameterfv(samplerObject, pname, params); +} + +void Context::getSamplerParameterfvRobust(SamplerID sampler, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + getSamplerParameterfv(sampler, pname, params); +} + +void Context::programParameteri(ShaderProgramID program, GLenum pname, GLint value) +{ + gl::Program *programObject = getProgramResolveLink(program); + SetProgramParameteri(programObject, pname, value); +} + +void Context::initRendererString() +{ + std::ostringstream frontendRendererString; + std::string vendorString(mDisplay->getBackendVendorString()); + std::string rendererString(mDisplay->getBackendRendererDescription()); + std::string versionString(mDisplay->getBackendVersionString(!isWebGL())); + // Commas are used as a separator in ANGLE's renderer string, so remove commas from each + // element. + vendorString.erase(std::remove(vendorString.begin(), vendorString.end(), ','), + vendorString.end()); + rendererString.erase(std::remove(rendererString.begin(), rendererString.end(), ','), + rendererString.end()); + versionString.erase(std::remove(versionString.begin(), versionString.end(), ','), + versionString.end()); + frontendRendererString << "ANGLE ("; + frontendRendererString << vendorString; + frontendRendererString << ", "; + frontendRendererString << rendererString; + frontendRendererString << ", "; + frontendRendererString << versionString; + frontendRendererString << ")"; + + mRendererString = MakeStaticString(frontendRendererString.str()); +} + +void Context::initVersionStrings() +{ + const Version &clientVersion = getClientVersion(); + + std::ostringstream versionString; + if (getClientType() == EGL_OPENGL_ES_API) + { + versionString << "OpenGL ES "; + } + versionString << clientVersion.major << "." << clientVersion.minor << ".0 (ANGLE " + << angle::GetANGLEVersionString() << ")"; + mVersionString = MakeStaticString(versionString.str()); + + std::ostringstream shadingLanguageVersionString; + if (getClientType() == EGL_OPENGL_ES_API) + { + shadingLanguageVersionString << "OpenGL ES GLSL ES "; + } + else + { + ASSERT(getClientType() == EGL_OPENGL_API); + shadingLanguageVersionString << "OpenGL GLSL "; + } + shadingLanguageVersionString << (clientVersion.major == 2 ? 1 : clientVersion.major) << "." + << clientVersion.minor << "0 (ANGLE " + << angle::GetANGLEVersionString() << ")"; + mShadingLanguageString = MakeStaticString(shadingLanguageVersionString.str()); +} + +void Context::initExtensionStrings() +{ + auto mergeExtensionStrings = [](const std::vector<const char *> &strings) { + std::ostringstream combinedStringStream; + std::copy(strings.begin(), strings.end(), + std::ostream_iterator<const char *>(combinedStringStream, " ")); + return MakeStaticString(combinedStringStream.str()); + }; + + mExtensionStrings.clear(); + for (const auto &extensionString : mState.mExtensions.getStrings()) + { + mExtensionStrings.push_back(MakeStaticString(extensionString)); + } + mExtensionString = mergeExtensionStrings(mExtensionStrings); + + mRequestableExtensionStrings.clear(); + for (const auto &extensionInfo : GetExtensionInfoMap()) + { + if (extensionInfo.second.Requestable && + !(mState.mExtensions.*(extensionInfo.second.ExtensionsMember)) && + mSupportedExtensions.*(extensionInfo.second.ExtensionsMember)) + { + mRequestableExtensionStrings.push_back(MakeStaticString(extensionInfo.first)); + } + } + mRequestableExtensionString = mergeExtensionStrings(mRequestableExtensionStrings); +} + +const GLubyte *Context::getString(GLenum name) +{ + return static_cast<const Context *>(this)->getString(name); +} + +const GLubyte *Context::getStringi(GLenum name, GLuint index) +{ + return static_cast<const Context *>(this)->getStringi(name, index); +} + +const GLubyte *Context::getString(GLenum name) const +{ + switch (name) + { + case GL_VENDOR: + return reinterpret_cast<const GLubyte *>(mDisplay->getVendorString().c_str()); + + case GL_RENDERER: + return reinterpret_cast<const GLubyte *>(mRendererString); + + case GL_VERSION: + return reinterpret_cast<const GLubyte *>(mVersionString); + + case GL_SHADING_LANGUAGE_VERSION: + return reinterpret_cast<const GLubyte *>(mShadingLanguageString); + + case GL_EXTENSIONS: + return reinterpret_cast<const GLubyte *>(mExtensionString); + + case GL_REQUESTABLE_EXTENSIONS_ANGLE: + return reinterpret_cast<const GLubyte *>(mRequestableExtensionString); + + case GL_SERIALIZED_CONTEXT_STRING_ANGLE: + if (angle::SerializeContextToString(this, &mCachedSerializedStateString) == + angle::Result::Continue) + { + return reinterpret_cast<const GLubyte *>(mCachedSerializedStateString.c_str()); + } + else + { + return nullptr; + } + + default: + UNREACHABLE(); + return nullptr; + } +} + +const GLubyte *Context::getStringi(GLenum name, GLuint index) const +{ + switch (name) + { + case GL_EXTENSIONS: + return reinterpret_cast<const GLubyte *>(mExtensionStrings[index]); + + case GL_REQUESTABLE_EXTENSIONS_ANGLE: + return reinterpret_cast<const GLubyte *>(mRequestableExtensionStrings[index]); + + default: + UNREACHABLE(); + return nullptr; + } +} + +size_t Context::getExtensionStringCount() const +{ + return mExtensionStrings.size(); +} + +bool Context::isExtensionRequestable(const char *name) const +{ + const ExtensionInfoMap &extensionInfos = GetExtensionInfoMap(); + auto extension = extensionInfos.find(name); + + return extension != extensionInfos.end() && extension->second.Requestable && + mSupportedExtensions.*(extension->second.ExtensionsMember); +} + +bool Context::isExtensionDisablable(const char *name) const +{ + const ExtensionInfoMap &extensionInfos = GetExtensionInfoMap(); + auto extension = extensionInfos.find(name); + + return extension != extensionInfos.end() && extension->second.Disablable && + mSupportedExtensions.*(extension->second.ExtensionsMember); +} + +void Context::requestExtension(const char *name) +{ + setExtensionEnabled(name, true); +} +void Context::disableExtension(const char *name) +{ + setExtensionEnabled(name, false); +} + +void Context::setExtensionEnabled(const char *name, bool enabled) +{ + // OVR_multiview is implicitly enabled when OVR_multiview2 is enabled + if (strcmp(name, "GL_OVR_multiview2") == 0) + { + setExtensionEnabled("GL_OVR_multiview", enabled); + } + const ExtensionInfoMap &extensionInfos = GetExtensionInfoMap(); + ASSERT(extensionInfos.find(name) != extensionInfos.end()); + const auto &extension = extensionInfos.at(name); + ASSERT(extension.Requestable); + ASSERT(isExtensionRequestable(name)); + + if (mState.mExtensions.*(extension.ExtensionsMember) == enabled) + { + // No change + return; + } + + mState.mExtensions.*(extension.ExtensionsMember) = enabled; + + reinitializeAfterExtensionsChanged(); +} + +void Context::reinitializeAfterExtensionsChanged() +{ + updateCaps(); + initExtensionStrings(); + + // Release the shader compiler so it will be re-created with the requested extensions enabled. + releaseShaderCompiler(); + + // Invalidate all textures and framebuffer. Some extensions make new formats renderable or + // sampleable. + mState.mTextureManager->signalAllTexturesDirty(); + for (auto &zeroTexture : mZeroTextures) + { + if (zeroTexture.get() != nullptr) + { + zeroTexture->signalDirtyStorage(InitState::Initialized); + } + } + + mState.mFramebufferManager->invalidateFramebufferCompletenessCache(); +} + +size_t Context::getRequestableExtensionStringCount() const +{ + return mRequestableExtensionStrings.size(); +} + +void Context::beginTransformFeedback(PrimitiveMode primitiveMode) +{ + TransformFeedback *transformFeedback = mState.getCurrentTransformFeedback(); + ASSERT(transformFeedback != nullptr); + ASSERT(!transformFeedback->isPaused()); + + // TODO: http://anglebug.com/7232: Handle PPOs + ANGLE_CONTEXT_TRY(transformFeedback->begin(this, primitiveMode, mState.getProgram())); + mStateCache.onActiveTransformFeedbackChange(this); +} + +bool Context::hasActiveTransformFeedback(ShaderProgramID program) const +{ + for (auto pair : mTransformFeedbackMap) + { + if (pair.second != nullptr && pair.second->hasBoundProgram(program)) + { + return true; + } + } + return false; +} + +Extensions Context::generateSupportedExtensions() const +{ + Extensions supportedExtensions = mImplementation->getNativeExtensions(); + + if (getClientVersion() < ES_2_0) + { + // Default extensions for GLES1 + supportedExtensions.pointSizeArrayOES = true; + supportedExtensions.textureCubeMapOES = true; + supportedExtensions.pointSpriteOES = true; + supportedExtensions.drawTextureOES = true; + supportedExtensions.framebufferObjectOES = true; + supportedExtensions.parallelShaderCompileKHR = false; + supportedExtensions.texture3DOES = false; + supportedExtensions.clipDistanceAPPLE = false; + } + + if (getClientVersion() < ES_3_0) + { + // Disable ES3+ extensions + supportedExtensions.colorBufferFloatEXT = false; + supportedExtensions.EGLImageExternalEssl3OES = false; + supportedExtensions.multiviewOVR = false; + supportedExtensions.multiview2OVR = false; + supportedExtensions.multiviewMultisampleANGLE = false; + supportedExtensions.copyTexture3dANGLE = false; + supportedExtensions.textureMultisampleANGLE = false; + supportedExtensions.drawBuffersIndexedEXT = false; + supportedExtensions.drawBuffersIndexedOES = false; + supportedExtensions.EGLImageArrayEXT = false; + supportedExtensions.textureFormatSRGBOverrideEXT = false; + + // Support GL_EXT_texture_norm16 on non-WebGL ES2 contexts. This is needed for R16/RG16 + // texturing for HDR video playback in Chromium which uses ES2 for compositor contexts. + // Remove this workaround after Chromium migrates to ES3 for compositor contexts. + if (mWebGLContext || getClientVersion() < ES_2_0) + { + supportedExtensions.textureNorm16EXT = false; + } + + // Requires immutable textures + supportedExtensions.yuvInternalFormatANGLE = false; + + // Require ESSL 3.0 + supportedExtensions.shaderMultisampleInterpolationOES = false; + supportedExtensions.shaderNoperspectiveInterpolationNV = false; + supportedExtensions.sampleVariablesOES = false; + + // Require ES 3.1 but could likely be exposed on 3.0 + supportedExtensions.textureCubeMapArrayEXT = false; + supportedExtensions.textureCubeMapArrayOES = false; + + // Require RED and RG formats + supportedExtensions.textureSRGBR8EXT = false; + supportedExtensions.textureSRGBRG8EXT = false; + + // Requires glCompressedTexImage3D + supportedExtensions.textureCompressionAstcOES = false; + + // Don't expose GL_EXT_texture_sRGB_decode without sRGB texture support + if (!supportedExtensions.sRGBEXT) + { + supportedExtensions.textureSRGBDecodeEXT = false; + } + + // Don't expose GL_OES_texture_float_linear without full legacy float texture support + // The renderer may report OES_texture_float_linear without OES_texture_float + // This is valid in a GLES 3.0 context, but not in a GLES 2.0 context + if (!(supportedExtensions.textureFloatOES && supportedExtensions.textureHalfFloatOES)) + { + supportedExtensions.textureFloatLinearOES = false; + supportedExtensions.textureHalfFloatLinearOES = false; + } + + // Because of the difference in the SNORM to FLOAT conversion formula + // between GLES 2.0 and 3.0, vertex type 10_10_10_2 is disabled + // when the context version is lower than 3.0 + supportedExtensions.vertexType1010102OES = false; + + // GL_EXT_EGL_image_storage requires ESSL3 + supportedExtensions.EGLImageStorageEXT = false; + + // GL_EXT_YUV_target requires ESSL3 + supportedExtensions.YUVTargetEXT = false; + + // GL_EXT_clip_cull_distance requires ESSL3 + supportedExtensions.clipCullDistanceEXT = false; + + // ANGLE_shader_pixel_local_storage requires ES3 + supportedExtensions.shaderPixelLocalStorageANGLE = false; + supportedExtensions.shaderPixelLocalStorageCoherentANGLE = false; + } + + if (getClientVersion() < ES_3_1) + { + // Disable ES3.1+ extensions + supportedExtensions.geometryShaderEXT = false; + supportedExtensions.geometryShaderOES = false; + supportedExtensions.gpuShader5EXT = false; + supportedExtensions.primitiveBoundingBoxEXT = false; + supportedExtensions.shaderImageAtomicOES = false; + supportedExtensions.shaderIoBlocksEXT = false; + supportedExtensions.shaderIoBlocksOES = false; + supportedExtensions.tessellationShaderEXT = false; + supportedExtensions.textureBufferEXT = false; + supportedExtensions.textureBufferOES = false; + + // TODO(http://anglebug.com/2775): Multisample arrays could be supported on ES 3.0 as well + // once 2D multisample texture extension is exposed there. + supportedExtensions.textureStorageMultisample2dArrayOES = false; + } + + if (getClientVersion() > ES_2_0) + { + // FIXME(geofflang): Don't support EXT_sRGB in non-ES2 contexts + // supportedExtensions.sRGB = false; + + // If colorBufferFloatEXT is disabled but colorBufferHalfFloatEXT is enabled, then we will + // expose some floating-point formats as color buffer targets but reject blits between + // fixed-point and floating-point formats (this behavior is only enabled in + // colorBufferFloatEXT, and must be rejected if only colorBufferHalfFloatEXT is enabled). + // dEQP does not check for this, and will assume that floating-point and fixed-point formats + // can be blit onto each other if the format is available. + // We require colorBufferFloatEXT to be present in order to enable colorBufferHalfFloatEXT, + // so that blitting is always allowed if the requested formats are exposed and have the + // correct feature capabilities. WebGL 2 wants to support colorBufferHalfFloatEXT without + // colorBufferFloatEXT. + if (!supportedExtensions.colorBufferFloatEXT && !mWebGLContext) + { + supportedExtensions.colorBufferHalfFloatEXT = false; + } + + // Disable support for CHROMIUM_color_buffer_float_rgb[a] in ES 3.0+, these extensions are + // non-conformant in ES 3.0 and superseded by EXT_color_buffer_float. + supportedExtensions.colorBufferFloatRgbCHROMIUM = false; + supportedExtensions.colorBufferFloatRgbaCHROMIUM = false; + } + + if (getFrontendFeatures().disableDrawBuffersIndexed.enabled) + { + supportedExtensions.drawBuffersIndexedEXT = false; + supportedExtensions.drawBuffersIndexedOES = false; + } + + if (getFrontendFeatures().disableAnisotropicFiltering.enabled) + { + supportedExtensions.textureFilterAnisotropicEXT = false; + } + + if (!getFrontendFeatures().emulatePixelLocalStorage.enabled) + { + supportedExtensions.shaderPixelLocalStorageANGLE = false; + supportedExtensions.shaderPixelLocalStorageCoherentANGLE = false; + } + + // Some extensions are always available because they are implemented in the GL layer. + supportedExtensions.bindUniformLocationCHROMIUM = true; + supportedExtensions.vertexArrayObjectOES = true; + supportedExtensions.bindGeneratesResourceCHROMIUM = true; + supportedExtensions.clientArraysANGLE = true; + supportedExtensions.requestExtensionANGLE = true; + supportedExtensions.multiDrawANGLE = true; + + // Enable the no error extension if the context was created with the flag. + supportedExtensions.noErrorKHR = mSkipValidation; + + // Enable surfaceless to advertise we'll have the correct behavior when there is no default FBO + supportedExtensions.surfacelessContextOES = mSurfacelessSupported; + + // Explicitly enable GL_KHR_debug + supportedExtensions.debugKHR = true; + + // Explicitly enable GL_EXT_debug_label + supportedExtensions.debugLabelEXT = true; + + // Explicitly enable GL_ANGLE_robust_client_memory if the context supports validation. + supportedExtensions.robustClientMemoryANGLE = !mSkipValidation; + + // Determine robust resource init availability from EGL. + supportedExtensions.robustResourceInitializationANGLE = mState.isRobustResourceInitEnabled(); + + // mState.mExtensions.robustBufferAccessBehaviorKHR is true only if robust access is true and + // the backend supports it. + supportedExtensions.robustBufferAccessBehaviorKHR = + mState.hasRobustAccess() && supportedExtensions.robustBufferAccessBehaviorKHR; + + // Enable the cache control query unconditionally. + supportedExtensions.programCacheControlANGLE = true; + + // If EGL_KHR_fence_sync is not enabled, don't expose GL_OES_EGL_sync. + ASSERT(mDisplay); + if (!mDisplay->getExtensions().fenceSync) + { + supportedExtensions.EGLSyncOES = false; + } + + if (mDisplay->getExtensions().robustnessVideoMemoryPurgeNV) + { + supportedExtensions.robustnessVideoMemoryPurgeNV = true; + } + + supportedExtensions.memorySizeANGLE = true; + + // GL_CHROMIUM_lose_context is implemented in the frontend + supportedExtensions.loseContextCHROMIUM = true; + + // The ASTC texture extensions have dependency requirements. + if (supportedExtensions.textureCompressionAstcHdrKHR || + supportedExtensions.textureCompressionAstcSliced3dKHR) + { + // GL_KHR_texture_compression_astc_hdr cannot be exposed without also exposing + // GL_KHR_texture_compression_astc_ldr + ASSERT(supportedExtensions.textureCompressionAstcLdrKHR); + } + + if (supportedExtensions.textureCompressionAstcOES) + { + // GL_OES_texture_compression_astc cannot be exposed without also exposing + // GL_KHR_texture_compression_astc_ldr and GL_KHR_texture_compression_astc_hdr + ASSERT(supportedExtensions.textureCompressionAstcLdrKHR); + ASSERT(supportedExtensions.textureCompressionAstcHdrKHR); + } + + // GL_KHR_protected_textures + // If EGL_KHR_protected_content is not supported then GL_EXT_protected_texture + // can not be supported. + if (!mDisplay->getExtensions().protectedContentEXT) + { + supportedExtensions.protectedTexturesEXT = false; + } + + // GL_ANGLE_get_tex_level_parameter is implemented in the front-end + supportedExtensions.getTexLevelParameterANGLE = true; + + // Always enabled. Will return a default string if capture is not enabled. + supportedExtensions.getSerializedContextStringANGLE = true; + + // Performance counter queries are always supported. Different groups exist on each back-end. + supportedExtensions.performanceMonitorAMD = true; + + // GL_ANDROID_extension_pack_es31a + supportedExtensions.extensionPackEs31aANDROID = + CanSupportAEP(getClientVersion(), supportedExtensions); + + return supportedExtensions; +} + +void Context::initCaps() +{ + mState.mCaps = mImplementation->getNativeCaps(); + + // TODO (http://anglebug.com/6010): mSupportedExtensions should not be modified here + mSupportedExtensions = generateSupportedExtensions(); + + if (!mDisplay->getFrontendFeatures().allowCompressedFormats.enabled) + { + INFO() << "Limiting compressed format support.\n"; + + mSupportedExtensions.compressedEACR11SignedTextureOES = false; + mSupportedExtensions.compressedEACR11UnsignedTextureOES = false; + mSupportedExtensions.compressedEACRG11SignedTextureOES = false; + mSupportedExtensions.compressedEACRG11UnsignedTextureOES = false; + mSupportedExtensions.compressedETC1RGB8SubTextureEXT = false; + mSupportedExtensions.compressedETC1RGB8TextureOES = false; + mSupportedExtensions.compressedETC2PunchthroughARGBA8TextureOES = false; + mSupportedExtensions.compressedETC2PunchthroughASRGB8AlphaTextureOES = false; + mSupportedExtensions.compressedETC2RGB8TextureOES = false; + mSupportedExtensions.compressedETC2RGBA8TextureOES = false; + mSupportedExtensions.compressedETC2SRGB8Alpha8TextureOES = false; + mSupportedExtensions.compressedETC2SRGB8TextureOES = false; + mSupportedExtensions.compressedTextureEtcANGLE = false; + mSupportedExtensions.textureCompressionPvrtcIMG = false; + mSupportedExtensions.pvrtcSRGBEXT = false; + mSupportedExtensions.copyCompressedTextureCHROMIUM = false; + mSupportedExtensions.textureCompressionAstcHdrKHR = false; + mSupportedExtensions.textureCompressionAstcLdrKHR = false; + mSupportedExtensions.textureCompressionAstcOES = false; + mSupportedExtensions.textureCompressionBptcEXT = false; + mSupportedExtensions.textureCompressionDxt1EXT = false; + mSupportedExtensions.textureCompressionDxt3ANGLE = false; + mSupportedExtensions.textureCompressionDxt5ANGLE = false; + mSupportedExtensions.textureCompressionRgtcEXT = false; + mSupportedExtensions.textureCompressionS3tcSrgbEXT = false; + mSupportedExtensions.textureCompressionAstcSliced3dKHR = false; + mSupportedExtensions.textureFilteringHintCHROMIUM = false; + + mState.mCaps.compressedTextureFormats.clear(); + } + + mState.mExtensions = mSupportedExtensions; + + mState.mLimitations = mImplementation->getNativeLimitations(); + + // GLES1 emulation: Initialize caps (Table 6.20 / 6.22 in the ES 1.1 spec) + if (getClientType() == EGL_OPENGL_API || getClientVersion() < Version(2, 0)) + { + mState.mCaps.maxMultitextureUnits = 4; + mState.mCaps.maxClipPlanes = 6; + mState.mCaps.maxLights = 8; + mState.mCaps.maxModelviewMatrixStackDepth = Caps::GlobalMatrixStackDepth; + mState.mCaps.maxProjectionMatrixStackDepth = Caps::GlobalMatrixStackDepth; + mState.mCaps.maxTextureMatrixStackDepth = Caps::GlobalMatrixStackDepth; + mState.mCaps.minSmoothPointSize = 1.0f; + mState.mCaps.maxSmoothPointSize = 1.0f; + mState.mCaps.minSmoothLineWidth = 1.0f; + mState.mCaps.maxSmoothLineWidth = 1.0f; + } + + mState.mCaps.maxDebugMessageLength = 1024; + mState.mCaps.maxDebugLoggedMessages = 1024; + mState.mCaps.maxDebugGroupStackDepth = 1024; + mState.mCaps.maxLabelLength = 1024; + + if (getClientVersion() < Version(3, 0)) + { + mState.mCaps.maxViews = 1u; + } + +#if 0 +// This logging can generate a lot of spam in test suites that create many contexts +# define ANGLE_LOG_LIMITED_CAP(cap, limit) \ + INFO() << "Limiting " << #cap << " to implementation limit " << (limit) << " (was " \ + << (cap) << ")." +#else +# define ANGLE_LOG_LIMITED_CAP(cap, limit) +#endif + +#define ANGLE_LIMIT_CAP(cap, limit) \ + do \ + { \ + if ((cap) > (limit)) \ + { \ + ANGLE_LOG_LIMITED_CAP(cap, limit); \ + (cap) = (limit); \ + } \ + } while (0) + + // Apply/Verify implementation limits + ANGLE_LIMIT_CAP(mState.mCaps.maxDrawBuffers, IMPLEMENTATION_MAX_DRAW_BUFFERS); + ANGLE_LIMIT_CAP(mState.mCaps.maxColorAttachments, IMPLEMENTATION_MAX_DRAW_BUFFERS); + ANGLE_LIMIT_CAP(mState.mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS); + ANGLE_LIMIT_CAP(mState.mCaps.maxVertexAttribStride, + static_cast<GLint>(limits::kMaxVertexAttribStride)); + + ASSERT(mState.mCaps.minAliasedPointSize >= 1.0f); + + if (getClientVersion() < ES_3_1) + { + mState.mCaps.maxVertexAttribBindings = mState.mCaps.maxVertexAttributes; + } + else + { + ANGLE_LIMIT_CAP(mState.mCaps.maxVertexAttribBindings, MAX_VERTEX_ATTRIB_BINDINGS); + } + + if (mWebGLContext && getLimitations().limitWebglMaxTextureSizeTo4096) + { + constexpr GLint kMaxTextureSize = 4096; + ANGLE_LIMIT_CAP(mState.mCaps.max2DTextureSize, kMaxTextureSize); + ANGLE_LIMIT_CAP(mState.mCaps.max3DTextureSize, kMaxTextureSize); + ANGLE_LIMIT_CAP(mState.mCaps.maxCubeMapTextureSize, kMaxTextureSize); + ANGLE_LIMIT_CAP(mState.mCaps.maxArrayTextureLayers, kMaxTextureSize); + ANGLE_LIMIT_CAP(mState.mCaps.maxRectangleTextureSize, kMaxTextureSize); + } + + ANGLE_LIMIT_CAP(mState.mCaps.max2DTextureSize, IMPLEMENTATION_MAX_2D_TEXTURE_SIZE); + ANGLE_LIMIT_CAP(mState.mCaps.maxCubeMapTextureSize, IMPLEMENTATION_MAX_CUBE_MAP_TEXTURE_SIZE); + ANGLE_LIMIT_CAP(mState.mCaps.max3DTextureSize, IMPLEMENTATION_MAX_3D_TEXTURE_SIZE); + ANGLE_LIMIT_CAP(mState.mCaps.maxArrayTextureLayers, IMPLEMENTATION_MAX_2D_ARRAY_TEXTURE_LAYERS); + ANGLE_LIMIT_CAP(mState.mCaps.maxRectangleTextureSize, IMPLEMENTATION_MAX_2D_TEXTURE_SIZE); + + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderUniformBlocks[ShaderType::Vertex], + IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS); + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderUniformBlocks[ShaderType::Geometry], + IMPLEMENTATION_MAX_GEOMETRY_SHADER_UNIFORM_BUFFERS); + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderUniformBlocks[ShaderType::Fragment], + IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS); + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderUniformBlocks[ShaderType::Compute], + IMPLEMENTATION_MAX_COMPUTE_SHADER_UNIFORM_BUFFERS); + ANGLE_LIMIT_CAP(mState.mCaps.maxCombinedUniformBlocks, + IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS); + ANGLE_LIMIT_CAP(mState.mCaps.maxUniformBufferBindings, + IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS); + + ANGLE_LIMIT_CAP(mState.mCaps.maxVertexOutputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4); + ANGLE_LIMIT_CAP(mState.mCaps.maxFragmentInputComponents, + IMPLEMENTATION_MAX_VARYING_VECTORS * 4); + + ANGLE_LIMIT_CAP(mState.mCaps.maxTransformFeedbackInterleavedComponents, + IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS); + ANGLE_LIMIT_CAP(mState.mCaps.maxTransformFeedbackSeparateAttributes, + IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS); + ANGLE_LIMIT_CAP(mState.mCaps.maxTransformFeedbackSeparateComponents, + IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS); + + if (getClientVersion() < ES_3_2 && !mState.mExtensions.tessellationShaderEXT) + { + ANGLE_LIMIT_CAP(mState.mCaps.maxCombinedTextureImageUnits, + IMPLEMENTATION_MAX_ES31_ACTIVE_TEXTURES); + } + else + { + ANGLE_LIMIT_CAP(mState.mCaps.maxCombinedTextureImageUnits, + IMPLEMENTATION_MAX_ACTIVE_TEXTURES); + } + + for (ShaderType shaderType : AllShaderTypes()) + { + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderTextureImageUnits[shaderType], + IMPLEMENTATION_MAX_SHADER_TEXTURES); + } + + ANGLE_LIMIT_CAP(mState.mCaps.maxImageUnits, IMPLEMENTATION_MAX_IMAGE_UNITS); + ANGLE_LIMIT_CAP(mState.mCaps.maxCombinedImageUniforms, IMPLEMENTATION_MAX_IMAGE_UNITS); + for (ShaderType shaderType : AllShaderTypes()) + { + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderImageUniforms[shaderType], + IMPLEMENTATION_MAX_IMAGE_UNITS); + } + + for (ShaderType shaderType : AllShaderTypes()) + { + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderAtomicCounterBuffers[shaderType], + IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS); + } + ANGLE_LIMIT_CAP(mState.mCaps.maxAtomicCounterBufferBindings, + IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS); + ANGLE_LIMIT_CAP(mState.mCaps.maxCombinedAtomicCounterBuffers, + IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS); + + for (ShaderType shaderType : AllShaderTypes()) + { + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderStorageBlocks[shaderType], + IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS); + } + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderStorageBufferBindings, + IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS); + ANGLE_LIMIT_CAP(mState.mCaps.maxCombinedShaderStorageBlocks, + IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS); + + ANGLE_LIMIT_CAP(mState.mCaps.maxClipDistances, IMPLEMENTATION_MAX_CLIP_DISTANCES); + + ANGLE_LIMIT_CAP(mState.mCaps.maxFramebufferLayers, IMPLEMENTATION_MAX_FRAMEBUFFER_LAYERS); + + ANGLE_LIMIT_CAP(mState.mCaps.maxSampleMaskWords, IMPLEMENTATION_MAX_SAMPLE_MASK_WORDS); + ANGLE_LIMIT_CAP(mState.mCaps.maxSamples, IMPLEMENTATION_MAX_SAMPLES); + ANGLE_LIMIT_CAP(mState.mCaps.maxFramebufferSamples, IMPLEMENTATION_MAX_SAMPLES); + ANGLE_LIMIT_CAP(mState.mCaps.maxColorTextureSamples, IMPLEMENTATION_MAX_SAMPLES); + ANGLE_LIMIT_CAP(mState.mCaps.maxDepthTextureSamples, IMPLEMENTATION_MAX_SAMPLES); + ANGLE_LIMIT_CAP(mState.mCaps.maxIntegerSamples, IMPLEMENTATION_MAX_SAMPLES); + + ANGLE_LIMIT_CAP(mState.mCaps.maxViews, IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS); + + ANGLE_LIMIT_CAP(mState.mCaps.maxDualSourceDrawBuffers, + IMPLEMENTATION_MAX_DUAL_SOURCE_DRAW_BUFFERS); + + // WebGL compatibility + mState.mExtensions.webglCompatibilityANGLE = mWebGLContext; + for (const auto &extensionInfo : GetExtensionInfoMap()) + { + // If the user has requested that extensions start disabled and they are requestable, + // disable them. + if (!mExtensionsEnabled && extensionInfo.second.Requestable) + { + mState.mExtensions.*(extensionInfo.second.ExtensionsMember) = false; + } + } + + // Hide emulated ETC1 extension from WebGL contexts. + if (mWebGLContext && getLimitations().emulatedEtc1) + { + mSupportedExtensions.compressedETC1RGB8SubTextureEXT = false; + mSupportedExtensions.compressedETC1RGB8TextureOES = false; + } + + if (getLimitations().emulatedAstc) + { + // Hide emulated ASTC extension from WebGL contexts. + if (mWebGLContext) + { + mSupportedExtensions.textureCompressionAstcLdrKHR = false; + mState.mExtensions.textureCompressionAstcLdrKHR = false; + } +#if !defined(ANGLE_HAS_ASTCENC) + // Don't expose emulated ASTC when it's not built. + mSupportedExtensions.textureCompressionAstcLdrKHR = false; + mState.mExtensions.textureCompressionAstcLdrKHR = false; +#endif + } + + // If we're capturing application calls for replay, apply some feature limits to increase + // portability of the trace. + if (getShareGroup()->getFrameCaptureShared()->enabled() || + getFrontendFeatures().enableCaptureLimits.enabled) + { + INFO() << "Limit some features because " + << (getShareGroup()->getFrameCaptureShared()->enabled() + ? "FrameCapture is enabled" + : "FrameCapture limits were forced") + << std::endl; + + if (!getFrontendFeatures().enableProgramBinaryForCapture.enabled) + { + // Some apps insist on being able to use glProgramBinary. For those, we'll allow the + // extension to remain on. Otherwise, force the extension off. + INFO() << "Disabling GL_OES_get_program_binary for trace portability"; + mDisplay->overrideFrontendFeatures({"disable_program_binary"}, true); + } + + // Set to the most common limit per gpuinfo.org. Required for several platforms we test. + constexpr GLint maxImageUnits = 8; + INFO() << "Limiting image unit count to " << maxImageUnits; + ANGLE_LIMIT_CAP(mState.mCaps.maxImageUnits, maxImageUnits); + for (ShaderType shaderType : AllShaderTypes()) + { + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderImageUniforms[shaderType], maxImageUnits); + } + + // Set a large uniform buffer offset alignment that works on multiple platforms. + // The offset used by the trace needs to be divisible by the device's actual value. + // Values seen during development: ARM (16), Intel (32), Qualcomm (128), Nvidia (256) + constexpr GLint uniformBufferOffsetAlignment = 256; + ASSERT(uniformBufferOffsetAlignment % mState.mCaps.uniformBufferOffsetAlignment == 0); + INFO() << "Setting uniform buffer offset alignment to " << uniformBufferOffsetAlignment; + mState.mCaps.uniformBufferOffsetAlignment = uniformBufferOffsetAlignment; + + // Also limit texture buffer offset alignment, if enabled + if (mState.mExtensions.textureBufferAny()) + { + constexpr GLint textureBufferOffsetAlignment = + gl::limits::kMinTextureBufferOffsetAlignment; + ASSERT(textureBufferOffsetAlignment % mState.mCaps.textureBufferOffsetAlignment == 0); + INFO() << "Setting texture buffer offset alignment to " << textureBufferOffsetAlignment; + mState.mCaps.textureBufferOffsetAlignment = textureBufferOffsetAlignment; + } + + INFO() << "Disabling GL_EXT_map_buffer_range and GL_OES_mapbuffer during capture, which " + "are not supported on some native drivers"; + mState.mExtensions.mapBufferRangeEXT = false; + mState.mExtensions.mapbufferOES = false; + + INFO() << "Disabling GL_CHROMIUM_bind_uniform_location during capture, which is not " + "supported on native drivers"; + mState.mExtensions.bindUniformLocationCHROMIUM = false; + + INFO() << "Disabling GL_NV_shader_noperspective_interpolation during capture, which is not " + "supported on some native drivers"; + mState.mExtensions.shaderNoperspectiveInterpolationNV = false; + + INFO() << "Disabling GL_NV_framebuffer_blit during capture, which is not " + "supported on some native drivers"; + mState.mExtensions.framebufferBlitNV = false; + + // NVIDIA's Vulkan driver only supports 4 draw buffers + constexpr GLint maxDrawBuffers = 4; + INFO() << "Limiting draw buffer count to " << maxDrawBuffers; + ANGLE_LIMIT_CAP(mState.mCaps.maxDrawBuffers, maxDrawBuffers); + + // Unity based applications are sending down GL streams with undefined behavior. + // Disabling EGL_KHR_create_context_no_error (which enables a new EGL attrib) prevents that, + // but we don't have the infrastructure for disabling EGL extensions yet. + // Instead, disable GL_KHR_no_error (which disables exposing the GL extension), which + // prevents writing invalid calls to the capture. + INFO() << "Enabling validation to prevent invalid calls from being captured. This " + "effectively disables GL_KHR_no_error and enables GL_ANGLE_robust_client_memory."; + mSkipValidation = false; + mState.mExtensions.noErrorKHR = mSkipValidation; + mState.mExtensions.robustClientMemoryANGLE = !mSkipValidation; + + INFO() << "Disabling GL_OES_depth32 during capture, which is not widely supported on " + "mobile"; + mState.mExtensions.depth32OES = false; + + // Pixel 4 (Qualcomm) only supports 6 atomic counter buffer bindings. + constexpr GLint maxAtomicCounterBufferBindings = 6; + INFO() << "Limiting max atomic counter buffer bindings to " + << maxAtomicCounterBufferBindings; + ANGLE_LIMIT_CAP(mState.mCaps.maxAtomicCounterBufferBindings, + maxAtomicCounterBufferBindings); + for (gl::ShaderType shaderType : gl::AllShaderTypes()) + { + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderAtomicCounterBuffers[shaderType], + maxAtomicCounterBufferBindings); + } + + // SwiftShader only supports 12 shader storage buffer bindings. + constexpr GLint maxShaderStorageBufferBindings = 12; + INFO() << "Limiting max shader storage buffer bindings to " + << maxShaderStorageBufferBindings; + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderStorageBufferBindings, + maxShaderStorageBufferBindings); + for (gl::ShaderType shaderType : gl::AllShaderTypes()) + { + ANGLE_LIMIT_CAP(mState.mCaps.maxShaderStorageBlocks[shaderType], + maxShaderStorageBufferBindings); + } + + // Pixel 4 only supports GL_MAX_SAMPLES of 4 + constexpr GLint maxSamples = 4; + INFO() << "Limiting GL_MAX_SAMPLES to " << maxSamples; + ANGLE_LIMIT_CAP(mState.mCaps.maxSamples, maxSamples); + } + + // Disable support for OES_get_program_binary + if (mDisplay->getFrontendFeatures().disableProgramBinary.enabled) + { + mState.mExtensions.getProgramBinaryOES = false; + mState.mCaps.shaderBinaryFormats.clear(); + mState.mCaps.programBinaryFormats.clear(); + mMemoryProgramCache = nullptr; + } + + if (mSupportedExtensions.shaderPixelLocalStorageANGLE) + { + int maxDrawableAttachments = + std::min(mState.mCaps.maxDrawBuffers, mState.mCaps.maxColorAttachments); + ShPixelLocalStorageType plsType = mImplementation->getNativePixelLocalStorageType(); + if (ShPixelLocalStorageTypeUsesImages(plsType)) + { + mState.mCaps.maxPixelLocalStoragePlanes = + mState.mCaps.maxShaderImageUniforms[ShaderType::Fragment]; + ANGLE_LIMIT_CAP(mState.mCaps.maxPixelLocalStoragePlanes, + IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES); + mState.mCaps.maxColorAttachmentsWithActivePixelLocalStorage = + mState.mCaps.maxColorAttachments; + mState.mCaps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes = std::min<GLint>( + mState.mCaps.maxPixelLocalStoragePlanes + + std::min(mState.mCaps.maxDrawBuffers, mState.mCaps.maxColorAttachments), + mState.mCaps.maxCombinedShaderOutputResources); + } + else + { + ASSERT(plsType == ShPixelLocalStorageType::FramebufferFetch); + mState.mCaps.maxPixelLocalStoragePlanes = maxDrawableAttachments; + ANGLE_LIMIT_CAP(mState.mCaps.maxPixelLocalStoragePlanes, + IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES); + if (!mSupportedExtensions.drawBuffersIndexedAny()) + { + // When pixel local storage is implemented as framebuffer attachments, we need to + // disable color masks and blending to its attachments. If the backend context + // doesn't have indexed blend and color mask support, then we will have have to + // disable them globally. This also means the application can't have its own draw + // buffers while PLS is active. + mState.mCaps.maxColorAttachmentsWithActivePixelLocalStorage = 0; + } + else + { + mState.mCaps.maxColorAttachmentsWithActivePixelLocalStorage = + maxDrawableAttachments - 1; + } + mState.mCaps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes = maxDrawableAttachments; + } + } + +#undef ANGLE_LIMIT_CAP +#undef ANGLE_LOG_CAP_LIMIT + + // Generate texture caps + updateCaps(); +} + +void Context::updateCaps() +{ + mState.mCaps.compressedTextureFormats.clear(); + mState.mTextureCaps.clear(); + + for (GLenum sizedInternalFormat : GetAllSizedInternalFormats()) + { + TextureCaps formatCaps = mImplementation->getNativeTextureCaps().get(sizedInternalFormat); + const InternalFormat &formatInfo = GetSizedInternalFormatInfo(sizedInternalFormat); + + // Update the format caps based on the client version and extensions. + // Caps are AND'd with the renderer caps because some core formats are still unsupported in + // ES3. + formatCaps.texturable = formatCaps.texturable && + formatInfo.textureSupport(getClientVersion(), mState.mExtensions); + formatCaps.filterable = formatCaps.filterable && + formatInfo.filterSupport(getClientVersion(), mState.mExtensions); + formatCaps.textureAttachment = + formatCaps.textureAttachment && + formatInfo.textureAttachmentSupport(getClientVersion(), mState.mExtensions); + formatCaps.renderbuffer = + formatCaps.renderbuffer && + formatInfo.renderbufferSupport(getClientVersion(), mState.mExtensions); + formatCaps.blendable = + formatCaps.blendable && formatInfo.blendSupport(getClientVersion(), mState.mExtensions); + + // OpenGL ES does not support multisampling with non-rendererable formats + // OpenGL ES 3.0 or prior does not support multisampling with integer formats + if (!formatCaps.renderbuffer || + (getClientVersion() < ES_3_1 && !mState.mExtensions.textureMultisampleANGLE && + formatInfo.isInt())) + { + formatCaps.sampleCounts.clear(); + } + else + { + // We may have limited the max samples for some required renderbuffer formats due to + // non-conformant formats. In this case MAX_SAMPLES needs to be lowered accordingly. + GLuint formatMaxSamples = formatCaps.getMaxSamples(); + + // GLES 3.0.5 section 4.4.2.2: "Implementations must support creation of renderbuffers + // in these required formats with up to the value of MAX_SAMPLES multisamples, with the + // exception of signed and unsigned integer formats." + if (!formatInfo.isInt() && formatInfo.isRequiredRenderbufferFormat(getClientVersion())) + { + ASSERT(getClientVersion() < ES_3_0 || formatMaxSamples >= 4); + mState.mCaps.maxSamples = + std::min(static_cast<GLuint>(mState.mCaps.maxSamples), formatMaxSamples); + } + + // Handle GLES 3.1 MAX_*_SAMPLES values similarly to MAX_SAMPLES. + if (getClientVersion() >= ES_3_1 || mState.mExtensions.textureMultisampleANGLE) + { + // GLES 3.1 section 9.2.5: "Implementations must support creation of renderbuffers + // in these required formats with up to the value of MAX_SAMPLES multisamples, with + // the exception that the signed and unsigned integer formats are required only to + // support creation of renderbuffers with up to the value of MAX_INTEGER_SAMPLES + // multisamples, which must be at least one." + if (formatInfo.isInt()) + { + mState.mCaps.maxIntegerSamples = std::min( + static_cast<GLuint>(mState.mCaps.maxIntegerSamples), formatMaxSamples); + } + + // GLES 3.1 section 19.3.1. + if (formatCaps.texturable) + { + if (formatInfo.depthBits > 0) + { + mState.mCaps.maxDepthTextureSamples = + std::min(static_cast<GLuint>(mState.mCaps.maxDepthTextureSamples), + formatMaxSamples); + } + else if (formatInfo.redBits > 0) + { + mState.mCaps.maxColorTextureSamples = + std::min(static_cast<GLuint>(mState.mCaps.maxColorTextureSamples), + formatMaxSamples); + } + } + } + } + + if (formatCaps.texturable && (formatInfo.compressed || formatInfo.paletted)) + { + mState.mCaps.compressedTextureFormats.push_back(sizedInternalFormat); + } + + mState.mTextureCaps.insert(sizedInternalFormat, formatCaps); + } + + // If program binary is disabled, blank out the memory cache pointer. + if (!mSupportedExtensions.getProgramBinaryOES) + { + mMemoryProgramCache = nullptr; + } + + // Compute which buffer types are allowed + mValidBufferBindings.reset(); + mValidBufferBindings.set(BufferBinding::ElementArray); + mValidBufferBindings.set(BufferBinding::Array); + + if (mState.mExtensions.pixelBufferObjectNV || getClientVersion() >= ES_3_0) + { + mValidBufferBindings.set(BufferBinding::PixelPack); + mValidBufferBindings.set(BufferBinding::PixelUnpack); + } + + if (getClientVersion() >= ES_3_0) + { + mValidBufferBindings.set(BufferBinding::CopyRead); + mValidBufferBindings.set(BufferBinding::CopyWrite); + mValidBufferBindings.set(BufferBinding::TransformFeedback); + mValidBufferBindings.set(BufferBinding::Uniform); + } + + if (getClientVersion() >= ES_3_1) + { + mValidBufferBindings.set(BufferBinding::AtomicCounter); + mValidBufferBindings.set(BufferBinding::ShaderStorage); + mValidBufferBindings.set(BufferBinding::DrawIndirect); + mValidBufferBindings.set(BufferBinding::DispatchIndirect); + } + + if (getClientVersion() >= ES_3_2 || mState.mExtensions.textureBufferAny()) + { + mValidBufferBindings.set(BufferBinding::Texture); + } + + if (!mState.mExtensions.parallelShaderCompileKHR) + { + mSingleThreadPool = angle::WorkerThreadPool::Create(false); + } + mMultiThreadPool = angle::WorkerThreadPool::Create( + mState.mExtensions.parallelShaderCompileKHR || + getFrontendFeatures().enableCompressingPipelineCacheInThreadPool.enabled); + + // Reinitialize some dirty bits that depend on extensions. + if (mState.isRobustResourceInitEnabled()) + { + mDrawDirtyObjects.set(State::DIRTY_OBJECT_DRAW_ATTACHMENTS); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES_INIT); + mDrawDirtyObjects.set(State::DIRTY_OBJECT_IMAGES_INIT); + mBlitDirtyObjects.set(State::DIRTY_OBJECT_DRAW_ATTACHMENTS); + mBlitDirtyObjects.set(State::DIRTY_OBJECT_READ_ATTACHMENTS); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES_INIT); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_IMAGES_INIT); + mReadPixelsDirtyObjects.set(State::DIRTY_OBJECT_READ_ATTACHMENTS); + mCopyImageDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); + mCopyImageDirtyObjects.set(State::DIRTY_OBJECT_READ_ATTACHMENTS); + } + + // We need to validate buffer bounds if we are in a WebGL or robust access context and the + // back-end does not support robust buffer access behaviour. + mBufferAccessValidationEnabled = (!mSupportedExtensions.robustBufferAccessBehaviorKHR && + (mState.isWebGL() || mState.hasRobustAccess())); + + // Cache this in the VertexArrays. They need to check it in state change notifications. + for (auto vaoIter : mVertexArrayMap) + { + VertexArray *vao = vaoIter.second; + vao->setBufferAccessValidationEnabled(mBufferAccessValidationEnabled); + } + + // Reinitialize state cache after extension changes. + mStateCache.initialize(this); +} + +bool Context::noopDrawInstanced(PrimitiveMode mode, GLsizei count, GLsizei instanceCount) const +{ + return (instanceCount == 0) || noopDraw(mode, count); +} + +angle::Result Context::prepareForClear(GLbitfield mask) +{ + // Sync the draw framebuffer manually after the clear attachments. + ANGLE_TRY(mState.getDrawFramebuffer()->ensureClearAttachmentsInitialized(this, mask)); + return syncStateForClear(); +} + +angle::Result Context::prepareForClearBuffer(GLenum buffer, GLint drawbuffer) +{ + // Sync the draw framebuffer manually after the clear attachments. + ANGLE_TRY(mState.getDrawFramebuffer()->ensureClearBufferAttachmentsInitialized(this, buffer, + drawbuffer)); + return syncStateForClear(); +} + +ANGLE_INLINE angle::Result Context::prepareForCopyImage() +{ + ANGLE_TRY(syncDirtyObjects(mCopyImageDirtyObjects, Command::CopyImage)); + return syncDirtyBits(mCopyImageDirtyBits, Command::CopyImage); +} + +ANGLE_INLINE angle::Result Context::prepareForDispatch() +{ + // Converting a PPO from graphics to compute requires re-linking it. + // The compute shader must have successfully linked before being included in the PPO, so no link + // errors that would have been caught during validation should be possible when re-linking the + // PPO with the compute shader. + Program *program = mState.getProgram(); + ProgramPipeline *pipeline = mState.getProgramPipeline(); + if (!program && pipeline) + { + // Linking the PPO can't fail due to a validation error within the compute program, + // since it successfully linked already in order to become part of the PPO in the first + // place. + pipeline->resolveLink(this); + ANGLE_CHECK(this, pipeline->isLinked(), "Program pipeline link failed", + GL_INVALID_OPERATION); + } + + ANGLE_TRY(syncDirtyObjects(mComputeDirtyObjects, Command::Dispatch)); + return syncDirtyBits(mComputeDirtyBits, Command::Dispatch); +} + +angle::Result Context::prepareForInvalidate(GLenum target) +{ + // Only sync the FBO that's being invalidated. Per the GLES3 spec, GL_FRAMEBUFFER is equivalent + // to GL_DRAW_FRAMEBUFFER for the purposes of invalidation. + GLenum effectiveTarget = target; + if (effectiveTarget == GL_FRAMEBUFFER) + { + effectiveTarget = GL_DRAW_FRAMEBUFFER; + } + ANGLE_TRY(mState.syncDirtyObject(this, effectiveTarget)); + return syncDirtyBits(effectiveTarget == GL_READ_FRAMEBUFFER ? mReadInvalidateDirtyBits + : mDrawInvalidateDirtyBits, + Command::Invalidate); +} + +angle::Result Context::syncState(const State::DirtyBits &bitMask, + const State::DirtyObjects &objectMask, + Command command) +{ + ANGLE_TRY(syncDirtyObjects(objectMask, command)); + ANGLE_TRY(syncDirtyBits(bitMask, command)); + return angle::Result::Continue; +} + +void Context::blitFramebuffer(GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) +{ + if (mask == 0) + { + // ES3.0 spec, section 4.3.2 specifies that a mask of zero is valid and no + // buffers are copied. + return; + } + + Framebuffer *drawFramebuffer = mState.getDrawFramebuffer(); + ASSERT(drawFramebuffer); + + // Note that blitting is called against draw framebuffer. + // See the code in gl::Context::blitFramebuffer. + if ((mask & GL_COLOR_BUFFER_BIT) && !drawFramebuffer->hasEnabledDrawBuffer()) + { + mask &= ~GL_COLOR_BUFFER_BIT; + } + + if ((mask & GL_STENCIL_BUFFER_BIT) && + drawFramebuffer->getState().getStencilAttachment() == nullptr) + { + mask &= ~GL_STENCIL_BUFFER_BIT; + } + + if ((mask & GL_DEPTH_BUFFER_BIT) && drawFramebuffer->getState().getDepthAttachment() == nullptr) + { + mask &= ~GL_DEPTH_BUFFER_BIT; + } + + // Early out if none of the specified attachments exist or are enabled. + if (mask == 0) + { + ANGLE_PERF_WARNING(mState.getDebug(), GL_DEBUG_SEVERITY_LOW, + "BlitFramebuffer called for non-existing buffers"); + return; + } + + Rectangle srcArea(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0); + Rectangle dstArea(dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0); + + if (dstArea.width == 0 || dstArea.height == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(syncStateForBlit(mask)); + ANGLE_CONTEXT_TRY(drawFramebuffer->blit(this, srcArea, dstArea, mask, filter)); +} + +void Context::blitFramebufferNV(GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) +{ + blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} + +void Context::clear(GLbitfield mask) +{ + if (mState.isRasterizerDiscardEnabled()) + { + return; + } + + // Noop empty scissors. + if (IsEmptyScissor(mState)) + { + return; + } + + // Remove clear bits that are ineffective. An effective clear changes at least one fragment. If + // color/depth/stencil masks make the clear ineffective we skip it altogether. + + // If all color channels in all draw buffers are masked, don't attempt to clear color. + if (mState.allActiveDrawBufferChannelsMasked()) + { + mask &= ~GL_COLOR_BUFFER_BIT; + } + + // If depth write is disabled, don't attempt to clear depth. + if (mState.getDrawFramebuffer()->getDepthAttachment() == nullptr || + !mState.getDepthStencilState().depthMask) + { + mask &= ~GL_DEPTH_BUFFER_BIT; + } + + // If all stencil bits are masked, don't attempt to clear stencil. + if (mState.getDrawFramebuffer()->getStencilAttachment() == nullptr || + (angle::BitMask<uint32_t>( + mState.getDrawFramebuffer()->getStencilAttachment()->getStencilSize()) & + mState.getDepthStencilState().stencilWritemask) == 0) + { + mask &= ~GL_STENCIL_BUFFER_BIT; + } + + if (mask == 0) + { + ANGLE_PERF_WARNING(mState.getDebug(), GL_DEBUG_SEVERITY_LOW, + "Clear called for non-existing buffers"); + return; + } + + ANGLE_CONTEXT_TRY(prepareForClear(mask)); + ANGLE_CONTEXT_TRY(mState.getDrawFramebuffer()->clear(this, mask)); +} + +bool Context::isClearBufferMaskedOut(GLenum buffer, GLint drawbuffer) const +{ + switch (buffer) + { + case GL_COLOR: + return IsColorMaskedOut(mState.getBlendStateExt(), drawbuffer); + case GL_DEPTH: + return mState.getDepthStencilState().isDepthMaskedOut(); + case GL_STENCIL: + return mState.getDepthStencilState().isStencilMaskedOut(); + case GL_DEPTH_STENCIL: + return mState.getDepthStencilState().isDepthMaskedOut() && + mState.getDepthStencilState().isStencilMaskedOut(); + default: + UNREACHABLE(); + return true; + } +} + +bool Context::noopClearBuffer(GLenum buffer, GLint drawbuffer) const +{ + Framebuffer *framebufferObject = mState.getDrawFramebuffer(); + + return !IsClearBufferEnabled(framebufferObject->getState(), buffer, drawbuffer) || + mState.isRasterizerDiscardEnabled() || isClearBufferMaskedOut(buffer, drawbuffer) || + IsEmptyScissor(mState); +} + +void Context::clearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *values) +{ + if (noopClearBuffer(buffer, drawbuffer)) + { + return; + } + + Framebuffer *framebufferObject = mState.getDrawFramebuffer(); + const FramebufferAttachment *attachment = nullptr; + if (buffer == GL_DEPTH) + { + attachment = framebufferObject->getDepthAttachment(); + } + else if (buffer == GL_COLOR && + static_cast<size_t>(drawbuffer) < framebufferObject->getNumColorAttachments()) + { + attachment = framebufferObject->getColorAttachment(drawbuffer); + } + // It's not an error to try to clear a non-existent buffer, but it's a no-op. We early out so + // that the backend doesn't need to take this case into account. + if (!attachment) + { + return; + } + ANGLE_CONTEXT_TRY(prepareForClearBuffer(buffer, drawbuffer)); + ANGLE_CONTEXT_TRY(framebufferObject->clearBufferfv(this, buffer, drawbuffer, values)); +} + +void Context::clearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint *values) +{ + if (noopClearBuffer(buffer, drawbuffer)) + { + return; + } + + Framebuffer *framebufferObject = mState.getDrawFramebuffer(); + const FramebufferAttachment *attachment = nullptr; + if (buffer == GL_COLOR && + static_cast<size_t>(drawbuffer) < framebufferObject->getNumColorAttachments()) + { + attachment = framebufferObject->getColorAttachment(drawbuffer); + } + // It's not an error to try to clear a non-existent buffer, but it's a no-op. We early out so + // that the backend doesn't need to take this case into account. + if (!attachment) + { + return; + } + ANGLE_CONTEXT_TRY(prepareForClearBuffer(buffer, drawbuffer)); + ANGLE_CONTEXT_TRY(framebufferObject->clearBufferuiv(this, buffer, drawbuffer, values)); +} + +void Context::clearBufferiv(GLenum buffer, GLint drawbuffer, const GLint *values) +{ + if (noopClearBuffer(buffer, drawbuffer)) + { + return; + } + + Framebuffer *framebufferObject = mState.getDrawFramebuffer(); + const FramebufferAttachment *attachment = nullptr; + if (buffer == GL_STENCIL) + { + attachment = framebufferObject->getStencilAttachment(); + } + else if (buffer == GL_COLOR && + static_cast<size_t>(drawbuffer) < framebufferObject->getNumColorAttachments()) + { + attachment = framebufferObject->getColorAttachment(drawbuffer); + } + // It's not an error to try to clear a non-existent buffer, but it's a no-op. We early out so + // that the backend doesn't need to take this case into account. + if (!attachment) + { + return; + } + ANGLE_CONTEXT_TRY(prepareForClearBuffer(buffer, drawbuffer)); + ANGLE_CONTEXT_TRY(framebufferObject->clearBufferiv(this, buffer, drawbuffer, values)); +} + +void Context::clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) +{ + if (noopClearBuffer(buffer, drawbuffer)) + { + return; + } + + Framebuffer *framebufferObject = mState.getDrawFramebuffer(); + ASSERT(framebufferObject); + + // If a buffer is not present, the clear has no effect + if (framebufferObject->getDepthAttachment() == nullptr && + framebufferObject->getStencilAttachment() == nullptr) + { + return; + } + + ANGLE_CONTEXT_TRY(prepareForClearBuffer(buffer, drawbuffer)); + ANGLE_CONTEXT_TRY(framebufferObject->clearBufferfi(this, buffer, drawbuffer, depth, stencil)); +} + +void Context::readPixels(GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + void *pixels) +{ + if (width == 0 || height == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(syncStateForReadPixels()); + + Framebuffer *readFBO = mState.getReadFramebuffer(); + ASSERT(readFBO); + + Rectangle area(x, y, width, height); + PixelPackState packState = mState.getPackState(); + Buffer *packBuffer = mState.getTargetBuffer(gl::BufferBinding::PixelPack); + ANGLE_CONTEXT_TRY(readFBO->readPixels(this, area, format, type, packState, packBuffer, pixels)); +} + +void Context::readPixelsRobust(GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLsizei bufSize, + GLsizei *length, + GLsizei *columns, + GLsizei *rows, + void *pixels) +{ + readPixels(x, y, width, height, format, type, pixels); +} + +void Context::readnPixelsRobust(GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLsizei bufSize, + GLsizei *length, + GLsizei *columns, + GLsizei *rows, + void *data) +{ + readPixels(x, y, width, height, format, type, data); +} + +void Context::copyTexImage2D(TextureTarget target, + GLint level, + GLenum internalformat, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLint border) +{ + ANGLE_CONTEXT_TRY(prepareForCopyImage()); + + Rectangle sourceArea(x, y, width, height); + + Framebuffer *framebuffer = mState.getReadFramebuffer(); + Texture *texture = getTextureByTarget(target); + ANGLE_CONTEXT_TRY( + texture->copyImage(this, target, level, sourceArea, internalformat, framebuffer)); +} + +void Context::copyTexSubImage2D(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + if (width == 0 || height == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(prepareForCopyImage()); + + Offset destOffset(xoffset, yoffset, 0); + Rectangle sourceArea(x, y, width, height); + + ImageIndex index = ImageIndex::MakeFromTarget(target, level, 1); + + Framebuffer *framebuffer = mState.getReadFramebuffer(); + Texture *texture = getTextureByTarget(target); + ANGLE_CONTEXT_TRY(texture->copySubImage(this, index, destOffset, sourceArea, framebuffer)); +} + +void Context::copyTexSubImage3D(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + if (width == 0 || height == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(prepareForCopyImage()); + + Offset destOffset(xoffset, yoffset, zoffset); + Rectangle sourceArea(x, y, width, height); + + ImageIndex index = ImageIndex::MakeFromType(TextureTargetToType(target), level, zoffset); + + Framebuffer *framebuffer = mState.getReadFramebuffer(); + Texture *texture = getTextureByTarget(target); + ANGLE_CONTEXT_TRY(texture->copySubImage(this, index, destOffset, sourceArea, framebuffer)); +} + +void Context::copyImageSubData(GLuint srcName, + GLenum srcTarget, + GLint srcLevel, + GLint srcX, + GLint srcY, + GLint srcZ, + GLuint dstName, + GLenum dstTarget, + GLint dstLevel, + GLint dstX, + GLint dstY, + GLint dstZ, + GLsizei srcWidth, + GLsizei srcHeight, + GLsizei srcDepth) +{ + // if copy region is zero, the copy is a successful no-op + if ((srcWidth == 0) || (srcHeight == 0) || (srcDepth == 0)) + { + return; + } + + if (srcTarget == GL_RENDERBUFFER) + { + // Source target is a Renderbuffer + Renderbuffer *readBuffer = getRenderbuffer(PackParam<RenderbufferID>(srcName)); + if (dstTarget == GL_RENDERBUFFER) + { + // Destination target is a Renderbuffer + Renderbuffer *writeBuffer = getRenderbuffer(PackParam<RenderbufferID>(dstName)); + + // Copy Renderbuffer to Renderbuffer + ANGLE_CONTEXT_TRY(writeBuffer->copyRenderbufferSubData( + this, readBuffer, srcLevel, srcX, srcY, srcZ, dstLevel, dstX, dstY, dstZ, srcWidth, + srcHeight, srcDepth)); + } + else + { + // Destination target is a Texture + ASSERT(dstTarget == GL_TEXTURE_2D || dstTarget == GL_TEXTURE_2D_ARRAY || + dstTarget == GL_TEXTURE_3D || dstTarget == GL_TEXTURE_CUBE_MAP); + + Texture *writeTexture = getTexture(PackParam<TextureID>(dstName)); + ANGLE_CONTEXT_TRY(syncTextureForCopy(writeTexture)); + + // Copy Renderbuffer to Texture + ANGLE_CONTEXT_TRY(writeTexture->copyRenderbufferSubData( + this, readBuffer, srcLevel, srcX, srcY, srcZ, dstLevel, dstX, dstY, dstZ, srcWidth, + srcHeight, srcDepth)); + } + } + else + { + // Source target is a Texture + ASSERT(srcTarget == GL_TEXTURE_2D || srcTarget == GL_TEXTURE_2D_ARRAY || + srcTarget == GL_TEXTURE_3D || srcTarget == GL_TEXTURE_CUBE_MAP); + + Texture *readTexture = getTexture(PackParam<TextureID>(srcName)); + ANGLE_CONTEXT_TRY(syncTextureForCopy(readTexture)); + + if (dstTarget == GL_RENDERBUFFER) + { + // Destination target is a Renderbuffer + Renderbuffer *writeBuffer = getRenderbuffer(PackParam<RenderbufferID>(dstName)); + + // Copy Texture to Renderbuffer + ANGLE_CONTEXT_TRY(writeBuffer->copyTextureSubData(this, readTexture, srcLevel, srcX, + srcY, srcZ, dstLevel, dstX, dstY, + dstZ, srcWidth, srcHeight, srcDepth)); + } + else + { + // Destination target is a Texture + ASSERT(dstTarget == GL_TEXTURE_2D || dstTarget == GL_TEXTURE_2D_ARRAY || + dstTarget == GL_TEXTURE_3D || dstTarget == GL_TEXTURE_CUBE_MAP); + + Texture *writeTexture = getTexture(PackParam<TextureID>(dstName)); + ANGLE_CONTEXT_TRY(syncTextureForCopy(writeTexture)); + + // Copy Texture to Texture + ANGLE_CONTEXT_TRY(writeTexture->copyTextureSubData( + this, readTexture, srcLevel, srcX, srcY, srcZ, dstLevel, dstX, dstY, dstZ, srcWidth, + srcHeight, srcDepth)); + } + } +} + +void Context::framebufferTexture2D(GLenum target, + GLenum attachment, + TextureTarget textarget, + TextureID texture, + GLint level) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture.value != 0) + { + Texture *textureObj = getTexture(texture); + ImageIndex index = ImageIndex::MakeFromTarget(textarget, level, 1); + framebuffer->setAttachment(this, GL_TEXTURE, attachment, index, textureObj); + } + else + { + framebuffer->resetAttachment(this, attachment); + } + + mState.setObjectDirty(target); +} + +void Context::framebufferTexture3D(GLenum target, + GLenum attachment, + TextureTarget textargetPacked, + TextureID texture, + GLint level, + GLint zoffset) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture.value != 0) + { + Texture *textureObj = getTexture(texture); + ImageIndex index = ImageIndex::Make3D(level, zoffset); + framebuffer->setAttachment(this, GL_TEXTURE, attachment, index, textureObj); + } + else + { + framebuffer->resetAttachment(this, attachment); + } + + mState.setObjectDirty(target); +} + +void Context::framebufferRenderbuffer(GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + RenderbufferID renderbuffer) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (renderbuffer.value != 0) + { + Renderbuffer *renderbufferObject = getRenderbuffer(renderbuffer); + GLsizei rbSamples = renderbufferObject->getState().getSamples(); + + framebuffer->setAttachmentMultisample(this, GL_RENDERBUFFER, attachment, gl::ImageIndex(), + renderbufferObject, rbSamples); + } + else + { + framebuffer->resetAttachment(this, attachment); + } + + mState.setObjectDirty(target); +} + +void Context::framebufferTextureLayer(GLenum target, + GLenum attachment, + TextureID texture, + GLint level, + GLint layer) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture.value != 0) + { + Texture *textureObject = getTexture(texture); + ImageIndex index = ImageIndex::MakeFromType(textureObject->getType(), level, layer); + framebuffer->setAttachment(this, GL_TEXTURE, attachment, index, textureObject); + } + else + { + framebuffer->resetAttachment(this, attachment); + } + + mState.setObjectDirty(target); +} + +void Context::framebufferTextureMultiview(GLenum target, + GLenum attachment, + TextureID texture, + GLint level, + GLint baseViewIndex, + GLsizei numViews) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture.value != 0) + { + Texture *textureObj = getTexture(texture); + + ImageIndex index; + if (textureObj->getType() == TextureType::_2DArray) + { + index = ImageIndex::Make2DArrayRange(level, baseViewIndex, numViews); + } + else + { + ASSERT(textureObj->getType() == TextureType::_2DMultisampleArray); + ASSERT(level == 0); + index = ImageIndex::Make2DMultisampleArrayRange(baseViewIndex, numViews); + } + framebuffer->setAttachmentMultiview(this, GL_TEXTURE, attachment, index, textureObj, + numViews, baseViewIndex); + } + else + { + framebuffer->resetAttachment(this, attachment); + } + + mState.setObjectDirty(target); +} + +void Context::framebufferTexture(GLenum target, GLenum attachment, TextureID texture, GLint level) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture.value != 0) + { + Texture *textureObj = getTexture(texture); + + ImageIndex index = ImageIndex::MakeFromType( + textureObj->getType(), level, ImageIndex::kEntireLevel, ImageIndex::kEntireLevel); + framebuffer->setAttachment(this, GL_TEXTURE, attachment, index, textureObj); + } + else + { + framebuffer->resetAttachment(this, attachment); + } + + mState.setObjectDirty(target); +} + +void Context::drawBuffers(GLsizei n, const GLenum *bufs) +{ + Framebuffer *framebuffer = mState.getDrawFramebuffer(); + ASSERT(framebuffer); + framebuffer->setDrawBuffers(n, bufs); + mState.setDrawFramebufferDirty(); + mStateCache.onDrawFramebufferChange(this); +} + +void Context::readBuffer(GLenum mode) +{ + Framebuffer *readFBO = mState.getReadFramebuffer(); + readFBO->setReadBuffer(mode); + mState.setObjectDirty(GL_READ_FRAMEBUFFER); +} + +void Context::discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments) +{ + // The specification isn't clear what should be done when the framebuffer isn't complete. + // We threat it the same way as GLES3 glInvalidateFramebuffer. + invalidateFramebuffer(target, numAttachments, attachments); +} + +void Context::invalidateFramebuffer(GLenum target, + GLsizei numAttachments, + const GLenum *attachments) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + // No-op incomplete FBOs. + if (!framebuffer->isComplete(this)) + { + return; + } + + ANGLE_CONTEXT_TRY(prepareForInvalidate(target)); + ANGLE_CONTEXT_TRY(framebuffer->invalidate(this, numAttachments, attachments)); +} + +void Context::invalidateSubFramebuffer(GLenum target, + GLsizei numAttachments, + const GLenum *attachments, + GLint x, + GLint y, + GLsizei width, + GLsizei height) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (!framebuffer->isComplete(this)) + { + return; + } + + Rectangle area(x, y, width, height); + ANGLE_CONTEXT_TRY(prepareForInvalidate(target)); + ANGLE_CONTEXT_TRY(framebuffer->invalidateSub(this, numAttachments, attachments, area)); +} + +void Context::texImage2D(TextureTarget target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const void *pixels) +{ + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + gl::Buffer *unpackBuffer = mState.getTargetBuffer(gl::BufferBinding::PixelUnpack); + + Extents size(width, height, 1); + Texture *texture = getTextureByTarget(target); + ANGLE_CONTEXT_TRY(texture->setImage(this, mState.getUnpackState(), unpackBuffer, target, level, + internalformat, size, format, type, + static_cast<const uint8_t *>(pixels))); +} + +void Context::texImage2DRobust(TextureTarget target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + GLsizei bufSize, + const void *pixels) +{ + texImage2D(target, level, internalformat, width, height, border, format, type, pixels); +} + +void Context::texImage3D(TextureTarget target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum format, + GLenum type, + const void *pixels) +{ + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + gl::Buffer *unpackBuffer = mState.getTargetBuffer(gl::BufferBinding::PixelUnpack); + + Extents size(width, height, depth); + Texture *texture = getTextureByTarget(target); + ANGLE_CONTEXT_TRY(texture->setImage(this, mState.getUnpackState(), unpackBuffer, target, level, + internalformat, size, format, type, + static_cast<const uint8_t *>(pixels))); +} + +void Context::texImage3DRobust(TextureTarget target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLenum format, + GLenum type, + GLsizei bufSize, + const void *pixels) +{ + texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels); +} + +void Context::texSubImage2D(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + const void *pixels) +{ + // Zero sized uploads are valid but no-ops + if (width == 0 || height == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + Box area(xoffset, yoffset, 0, width, height, 1); + Texture *texture = getTextureByTarget(target); + + gl::Buffer *unpackBuffer = mState.getTargetBuffer(gl::BufferBinding::PixelUnpack); + + ANGLE_CONTEXT_TRY(texture->setSubImage(this, mState.getUnpackState(), unpackBuffer, target, + level, area, format, type, + static_cast<const uint8_t *>(pixels))); +} + +void Context::texSubImage2DRobust(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLsizei bufSize, + const void *pixels) +{ + texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); +} + +void Context::texSubImage3D(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLenum type, + const void *pixels) +{ + // Zero sized uploads are valid but no-ops + if (width == 0 || height == 0 || depth == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + Box area(xoffset, yoffset, zoffset, width, height, depth); + Texture *texture = getTextureByTarget(target); + + gl::Buffer *unpackBuffer = mState.getTargetBuffer(gl::BufferBinding::PixelUnpack); + + ANGLE_CONTEXT_TRY(texture->setSubImage(this, mState.getUnpackState(), unpackBuffer, target, + level, area, format, type, + static_cast<const uint8_t *>(pixels))); +} + +void Context::texSubImage3DRobust(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLenum type, + GLsizei bufSize, + const void *pixels) +{ + texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, + pixels); +} + +void Context::compressedTexImage2D(TextureTarget target, + GLint level, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLsizei imageSize, + const void *data) +{ + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + Extents size(width, height, 1); + Texture *texture = getTextureByTarget(target); + // From OpenGL ES 3 spec: All pixel storage modes are ignored when decoding a compressed texture + // image. So we use an empty PixelUnpackState. + ANGLE_CONTEXT_TRY(texture->setCompressedImage(this, PixelUnpackState(), target, level, + internalformat, size, imageSize, + static_cast<const uint8_t *>(data))); +} + +void Context::compressedTexImage2DRobust(TextureTarget target, + GLint level, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLsizei imageSize, + GLsizei dataSize, + const GLvoid *data) +{ + compressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); +} + +void Context::compressedTexImage3D(TextureTarget target, + GLint level, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLsizei imageSize, + const void *data) +{ + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + Extents size(width, height, depth); + Texture *texture = getTextureByTarget(target); + // From OpenGL ES 3 spec: All pixel storage modes are ignored when decoding a compressed texture + // image. So we use an empty PixelUnpackState. + ANGLE_CONTEXT_TRY(texture->setCompressedImage(this, PixelUnpackState(), target, level, + internalformat, size, imageSize, + static_cast<const uint8_t *>(data))); +} + +void Context::compressedTexImage3DRobust(TextureTarget target, + GLint level, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLint border, + GLsizei imageSize, + GLsizei dataSize, + const GLvoid *data) +{ + compressedTexImage3D(target, level, internalformat, width, height, depth, border, imageSize, + data); +} + +void Context::compressedTexSubImage2D(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLsizei imageSize, + const void *data) +{ + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + Box area(xoffset, yoffset, 0, width, height, 1); + Texture *texture = getTextureByTarget(target); + // From OpenGL ES 3 spec: All pixel storage modes are ignored when decoding a compressed texture + // image. So we use an empty PixelUnpackState. + ANGLE_CONTEXT_TRY(texture->setCompressedSubImage(this, PixelUnpackState(), target, level, area, + format, imageSize, + static_cast<const uint8_t *>(data))); +} + +void Context::compressedTexSubImage2DRobust(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLsizei imageSize, + GLsizei dataSize, + const GLvoid *data) +{ + compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, + data); +} + +void Context::compressedTexSubImage3D(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLsizei imageSize, + const void *data) +{ + // Zero sized uploads are valid but no-ops + if (width == 0 || height == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + Box area(xoffset, yoffset, zoffset, width, height, depth); + Texture *texture = getTextureByTarget(target); + // From OpenGL ES 3 spec: All pixel storage modes are ignored when decoding a compressed texture + // image. So we use an empty PixelUnpackState. + ANGLE_CONTEXT_TRY(texture->setCompressedSubImage(this, PixelUnpackState(), target, level, area, + format, imageSize, + static_cast<const uint8_t *>(data))); +} + +void Context::compressedTexSubImage3DRobust(TextureTarget target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLsizei imageSize, + GLsizei dataSize, + const GLvoid *data) +{ + compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, + imageSize, data); +} + +void Context::generateMipmap(TextureType target) +{ + Texture *texture = getTextureByType(target); + ANGLE_CONTEXT_TRY(texture->generateMipmap(this)); +} + +void Context::copyTexture(TextureID sourceId, + GLint sourceLevel, + TextureTarget destTarget, + TextureID destId, + GLint destLevel, + GLint internalFormat, + GLenum destType, + GLboolean unpackFlipY, + GLboolean unpackPremultiplyAlpha, + GLboolean unpackUnmultiplyAlpha) +{ + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + gl::Texture *sourceTexture = getTexture(sourceId); + gl::Texture *destTexture = getTexture(destId); + ANGLE_CONTEXT_TRY( + destTexture->copyTexture(this, destTarget, destLevel, internalFormat, destType, sourceLevel, + ConvertToBool(unpackFlipY), ConvertToBool(unpackPremultiplyAlpha), + ConvertToBool(unpackUnmultiplyAlpha), sourceTexture)); +} + +void Context::copySubTexture(TextureID sourceId, + GLint sourceLevel, + TextureTarget destTarget, + TextureID destId, + GLint destLevel, + GLint xoffset, + GLint yoffset, + GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLboolean unpackFlipY, + GLboolean unpackPremultiplyAlpha, + GLboolean unpackUnmultiplyAlpha) +{ + // Zero sized copies are valid but no-ops + if (width == 0 || height == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + gl::Texture *sourceTexture = getTexture(sourceId); + gl::Texture *destTexture = getTexture(destId); + Offset offset(xoffset, yoffset, 0); + Box box(x, y, 0, width, height, 1); + ANGLE_CONTEXT_TRY(destTexture->copySubTexture( + this, destTarget, destLevel, offset, sourceLevel, box, ConvertToBool(unpackFlipY), + ConvertToBool(unpackPremultiplyAlpha), ConvertToBool(unpackUnmultiplyAlpha), + sourceTexture)); +} + +void Context::copyTexture3D(TextureID sourceId, + GLint sourceLevel, + TextureTarget destTarget, + TextureID destId, + GLint destLevel, + GLint internalFormat, + GLenum destType, + GLboolean unpackFlipY, + GLboolean unpackPremultiplyAlpha, + GLboolean unpackUnmultiplyAlpha) +{ + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + Texture *sourceTexture = getTexture(sourceId); + Texture *destTexture = getTexture(destId); + ANGLE_CONTEXT_TRY( + destTexture->copyTexture(this, destTarget, destLevel, internalFormat, destType, sourceLevel, + ConvertToBool(unpackFlipY), ConvertToBool(unpackPremultiplyAlpha), + ConvertToBool(unpackUnmultiplyAlpha), sourceTexture)); +} + +void Context::copySubTexture3D(TextureID sourceId, + GLint sourceLevel, + TextureTarget destTarget, + TextureID destId, + GLint destLevel, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLint x, + GLint y, + GLint z, + GLsizei width, + GLsizei height, + GLsizei depth, + GLboolean unpackFlipY, + GLboolean unpackPremultiplyAlpha, + GLboolean unpackUnmultiplyAlpha) +{ + // Zero sized copies are valid but no-ops + if (width == 0 || height == 0 || depth == 0) + { + return; + } + + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + Texture *sourceTexture = getTexture(sourceId); + Texture *destTexture = getTexture(destId); + Offset offset(xoffset, yoffset, zoffset); + Box box(x, y, z, width, height, depth); + ANGLE_CONTEXT_TRY(destTexture->copySubTexture( + this, destTarget, destLevel, offset, sourceLevel, box, ConvertToBool(unpackFlipY), + ConvertToBool(unpackPremultiplyAlpha), ConvertToBool(unpackUnmultiplyAlpha), + sourceTexture)); +} + +void Context::compressedCopyTexture(TextureID sourceId, TextureID destId) +{ + ANGLE_CONTEXT_TRY(syncStateForTexImage()); + + gl::Texture *sourceTexture = getTexture(sourceId); + gl::Texture *destTexture = getTexture(destId); + ANGLE_CONTEXT_TRY(destTexture->copyCompressedTexture(this, sourceTexture)); +} + +void Context::getBufferPointerv(BufferBinding target, GLenum pname, void **params) +{ + Buffer *buffer = mState.getTargetBuffer(target); + ASSERT(buffer); + + QueryBufferPointerv(buffer, pname, params); +} + +void Context::getBufferPointervRobust(BufferBinding target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + void **params) +{ + getBufferPointerv(target, pname, params); +} + +void *Context::mapBuffer(BufferBinding target, GLenum access) +{ + Buffer *buffer = mState.getTargetBuffer(target); + ASSERT(buffer); + + if (buffer->map(this, access) == angle::Result::Stop) + { + return nullptr; + } + + return buffer->getMapPointer(); +} + +GLboolean Context::unmapBuffer(BufferBinding target) +{ + Buffer *buffer = mState.getTargetBuffer(target); + ASSERT(buffer); + + GLboolean result; + if (buffer->unmap(this, &result) == angle::Result::Stop) + { + return GL_FALSE; + } + + return result; +} + +void *Context::mapBufferRange(BufferBinding target, + GLintptr offset, + GLsizeiptr length, + GLbitfield access) +{ + Buffer *buffer = mState.getTargetBuffer(target); + ASSERT(buffer); + + if (buffer->mapRange(this, offset, length, access) == angle::Result::Stop) + { + return nullptr; + } + + return buffer->getMapPointer(); +} + +void Context::flushMappedBufferRange(BufferBinding /*target*/, + GLintptr /*offset*/, + GLsizeiptr /*length*/) +{ + // We do not currently support a non-trivial implementation of FlushMappedBufferRange +} + +angle::Result Context::syncStateForReadPixels() +{ + return syncState(mReadPixelsDirtyBits, mReadPixelsDirtyObjects, Command::ReadPixels); +} + +angle::Result Context::syncStateForTexImage() +{ + return syncState(mTexImageDirtyBits, mTexImageDirtyObjects, Command::TexImage); +} + +angle::Result Context::syncStateForBlit(GLbitfield mask) +{ + uint32_t commandMask = 0; + if ((mask & GL_COLOR_BUFFER_BIT) != 0) + { + commandMask |= CommandBlitBufferColor; + } + if ((mask & GL_DEPTH_BUFFER_BIT) != 0) + { + commandMask |= CommandBlitBufferDepth; + } + if ((mask & GL_STENCIL_BUFFER_BIT) != 0) + { + commandMask |= CommandBlitBufferStencil; + } + + Command command = static_cast<Command>(static_cast<uint32_t>(Command::Blit) + commandMask); + + return syncState(mBlitDirtyBits, mBlitDirtyObjects, command); +} + +angle::Result Context::syncStateForClear() +{ + return syncState(mClearDirtyBits, mClearDirtyObjects, Command::Clear); +} + +angle::Result Context::syncTextureForCopy(Texture *texture) +{ + ASSERT(texture); + // Sync texture not active but scheduled for a copy + if (texture->hasAnyDirtyBit()) + { + return texture->syncState(this, Command::Other); + } + + return angle::Result::Continue; +} + +void Context::activeShaderProgram(ProgramPipelineID pipeline, ShaderProgramID program) +{ + Program *shaderProgram = getProgramNoResolveLink(program); + ProgramPipeline *programPipeline = + mState.mProgramPipelineManager->checkProgramPipelineAllocation(mImplementation.get(), + pipeline); + ASSERT(programPipeline); + + programPipeline->activeShaderProgram(shaderProgram); +} + +void Context::activeTexture(GLenum texture) +{ + mState.setActiveSampler(texture - GL_TEXTURE0); +} + +void Context::blendBarrier() +{ + mImplementation->blendBarrier(); +} + +void Context::blendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +{ + mState.setBlendColor(red, green, blue, alpha); +} + +void Context::blendEquation(GLenum mode) +{ + mState.setBlendEquation(mode, mode); + + mStateCache.onBlendEquationChange(this); +} + +void Context::blendEquationi(GLuint buf, GLenum mode) +{ + mState.setBlendEquationIndexed(mode, mode, buf); + + mStateCache.onBlendEquationChange(this); +} + +void Context::blendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) +{ + mState.setBlendEquation(modeRGB, modeAlpha); +} + +void Context::blendEquationSeparatei(GLuint buf, GLenum modeRGB, GLenum modeAlpha) +{ + mState.setBlendEquationIndexed(modeRGB, modeAlpha, buf); +} + +void Context::blendFunc(GLenum sfactor, GLenum dfactor) +{ + mState.setBlendFactors(sfactor, dfactor, sfactor, dfactor); +} + +void Context::blendFunci(GLuint buf, GLenum src, GLenum dst) +{ + mState.setBlendFactorsIndexed(src, dst, src, dst, buf); + + if (mState.noSimultaneousConstantColorAndAlphaBlendFunc()) + { + mStateCache.onBlendFuncIndexedChange(this); + } +} + +void Context::blendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +{ + mState.setBlendFactors(srcRGB, dstRGB, srcAlpha, dstAlpha); +} + +void Context::blendFuncSeparatei(GLuint buf, + GLenum srcRGB, + GLenum dstRGB, + GLenum srcAlpha, + GLenum dstAlpha) +{ + mState.setBlendFactorsIndexed(srcRGB, dstRGB, srcAlpha, dstAlpha, buf); + + if (mState.noSimultaneousConstantColorAndAlphaBlendFunc()) + { + mStateCache.onBlendFuncIndexedChange(this); + } +} + +void Context::clearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +{ + mState.setColorClearValue(red, green, blue, alpha); +} + +void Context::clearDepthf(GLfloat depth) +{ + mState.setDepthClearValue(clamp01(depth)); +} + +void Context::clearStencil(GLint s) +{ + mState.setStencilClearValue(s); +} + +void Context::colorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) +{ + mState.setColorMask(ConvertToBool(red), ConvertToBool(green), ConvertToBool(blue), + ConvertToBool(alpha)); + mStateCache.onColorMaskChange(this); +} + +void Context::colorMaski(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) +{ + mState.setColorMaskIndexed(ConvertToBool(r), ConvertToBool(g), ConvertToBool(b), + ConvertToBool(a), index); + mStateCache.onColorMaskChange(this); +} + +void Context::cullFace(CullFaceMode mode) +{ + mState.setCullMode(mode); +} + +void Context::depthFunc(GLenum func) +{ + mState.setDepthFunc(func); +} + +void Context::depthMask(GLboolean flag) +{ + mState.setDepthMask(ConvertToBool(flag)); +} + +void Context::depthRangef(GLfloat zNear, GLfloat zFar) +{ + mState.setDepthRange(clamp01(zNear), clamp01(zFar)); +} + +void Context::clipControl(GLenum origin, GLenum depth) +{ + mState.setClipControl(origin, depth); +} + +void Context::disable(GLenum cap) +{ + mState.setEnableFeature(cap, false); + mStateCache.onContextCapChange(this); +} + +void Context::disablei(GLenum target, GLuint index) +{ + mState.setEnableFeatureIndexed(target, false, index); + mStateCache.onContextCapChange(this); +} + +void Context::disableVertexAttribArray(GLuint index) +{ + mState.setEnableVertexAttribArray(index, false); + mStateCache.onVertexArrayStateChange(this); +} + +void Context::enable(GLenum cap) +{ + mState.setEnableFeature(cap, true); + mStateCache.onContextCapChange(this); +} + +void Context::enablei(GLenum target, GLuint index) +{ + mState.setEnableFeatureIndexed(target, true, index); + mStateCache.onContextCapChange(this); +} + +void Context::enableVertexAttribArray(GLuint index) +{ + mState.setEnableVertexAttribArray(index, true); + mStateCache.onVertexArrayStateChange(this); +} + +void Context::frontFace(GLenum mode) +{ + mState.setFrontFace(mode); +} + +void Context::hint(GLenum target, GLenum mode) +{ + switch (target) + { + case GL_GENERATE_MIPMAP_HINT: + mState.setGenerateMipmapHint(mode); + break; + + case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES: + mState.setFragmentShaderDerivativeHint(mode); + break; + + case GL_PERSPECTIVE_CORRECTION_HINT: + case GL_POINT_SMOOTH_HINT: + case GL_LINE_SMOOTH_HINT: + case GL_FOG_HINT: + mState.gles1().setHint(target, mode); + break; + case GL_TEXTURE_FILTERING_HINT_CHROMIUM: + mState.setTextureFilteringHint(mode); + break; + default: + UNREACHABLE(); + return; + } +} + +void Context::lineWidth(GLfloat width) +{ + mState.setLineWidth(width); +} + +void Context::pixelStorei(GLenum pname, GLint param) +{ + switch (pname) + { + case GL_UNPACK_ALIGNMENT: + mState.setUnpackAlignment(param); + break; + + case GL_PACK_ALIGNMENT: + mState.setPackAlignment(param); + break; + + case GL_PACK_REVERSE_ROW_ORDER_ANGLE: + mState.setPackReverseRowOrder(param != 0); + break; + + case GL_UNPACK_ROW_LENGTH: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().unpackSubimageEXT); + mState.setUnpackRowLength(param); + break; + + case GL_UNPACK_IMAGE_HEIGHT: + ASSERT(getClientMajorVersion() >= 3); + mState.setUnpackImageHeight(param); + break; + + case GL_UNPACK_SKIP_IMAGES: + ASSERT(getClientMajorVersion() >= 3); + mState.setUnpackSkipImages(param); + break; + + case GL_UNPACK_SKIP_ROWS: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().unpackSubimageEXT); + mState.setUnpackSkipRows(param); + break; + + case GL_UNPACK_SKIP_PIXELS: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().unpackSubimageEXT); + mState.setUnpackSkipPixels(param); + break; + + case GL_PACK_ROW_LENGTH: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().packSubimageNV); + mState.setPackRowLength(param); + break; + + case GL_PACK_SKIP_ROWS: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().packSubimageNV); + mState.setPackSkipRows(param); + break; + + case GL_PACK_SKIP_PIXELS: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().packSubimageNV); + mState.setPackSkipPixels(param); + break; + + default: + UNREACHABLE(); + return; + } +} + +void Context::polygonOffset(GLfloat factor, GLfloat units) +{ + mState.setPolygonOffsetParams(factor, units); +} + +void Context::sampleCoverage(GLfloat value, GLboolean invert) +{ + mState.setSampleCoverageParams(clamp01(value), ConvertToBool(invert)); +} + +void Context::sampleMaski(GLuint maskNumber, GLbitfield mask) +{ + mState.setSampleMaskParams(maskNumber, mask); +} + +void Context::scissor(GLint x, GLint y, GLsizei width, GLsizei height) +{ + mState.setScissorParams(x, y, width, height); +} + +void Context::shadingRateQCOM(GLenum rate) +{ + mState.setShadingRate(rate); +} + +void Context::stencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) +{ + GLint clampedRef = gl::clamp(ref, 0, std::numeric_limits<uint8_t>::max()); + if (face == GL_FRONT || face == GL_FRONT_AND_BACK) + { + mState.setStencilParams(func, clampedRef, mask); + } + + if (face == GL_BACK || face == GL_FRONT_AND_BACK) + { + mState.setStencilBackParams(func, clampedRef, mask); + } + + mStateCache.onStencilStateChange(this); +} + +void Context::stencilMaskSeparate(GLenum face, GLuint mask) +{ + if (face == GL_FRONT || face == GL_FRONT_AND_BACK) + { + mState.setStencilWritemask(mask); + } + + if (face == GL_BACK || face == GL_FRONT_AND_BACK) + { + mState.setStencilBackWritemask(mask); + } + + mStateCache.onStencilStateChange(this); +} + +void Context::stencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +{ + if (face == GL_FRONT || face == GL_FRONT_AND_BACK) + { + mState.setStencilOperations(fail, zfail, zpass); + } + + if (face == GL_BACK || face == GL_FRONT_AND_BACK) + { + mState.setStencilBackOperations(fail, zfail, zpass); + } +} + +void Context::vertexAttrib1f(GLuint index, GLfloat x) +{ + GLfloat vals[4] = {x, 0, 0, 1}; + mState.setVertexAttribf(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttrib1fv(GLuint index, const GLfloat *values) +{ + GLfloat vals[4] = {values[0], 0, 0, 1}; + mState.setVertexAttribf(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttrib2f(GLuint index, GLfloat x, GLfloat y) +{ + GLfloat vals[4] = {x, y, 0, 1}; + mState.setVertexAttribf(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttrib2fv(GLuint index, const GLfloat *values) +{ + GLfloat vals[4] = {values[0], values[1], 0, 1}; + mState.setVertexAttribf(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) +{ + GLfloat vals[4] = {x, y, z, 1}; + mState.setVertexAttribf(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttrib3fv(GLuint index, const GLfloat *values) +{ + GLfloat vals[4] = {values[0], values[1], values[2], 1}; + mState.setVertexAttribf(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + GLfloat vals[4] = {x, y, z, w}; + mState.setVertexAttribf(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttrib4fv(GLuint index, const GLfloat *values) +{ + mState.setVertexAttribf(index, values); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttribPointer(GLuint index, + GLint size, + VertexAttribType type, + GLboolean normalized, + GLsizei stride, + const void *ptr) +{ + mState.setVertexAttribPointer(this, index, mState.getTargetBuffer(BufferBinding::Array), size, + type, ConvertToBool(normalized), stride, ptr); + mStateCache.onVertexArrayStateChange(this); +} + +void Context::vertexAttribFormat(GLuint attribIndex, + GLint size, + VertexAttribType type, + GLboolean normalized, + GLuint relativeOffset) +{ + mState.setVertexAttribFormat(attribIndex, size, type, ConvertToBool(normalized), false, + relativeOffset); + mStateCache.onVertexArrayFormatChange(this); +} + +void Context::vertexAttribIFormat(GLuint attribIndex, + GLint size, + VertexAttribType type, + GLuint relativeOffset) +{ + mState.setVertexAttribFormat(attribIndex, size, type, false, true, relativeOffset); + mStateCache.onVertexArrayFormatChange(this); +} + +void Context::vertexAttribBinding(GLuint attribIndex, GLuint bindingIndex) +{ + mState.setVertexAttribBinding(this, attribIndex, bindingIndex); + mStateCache.onVertexArrayStateChange(this); +} + +void Context::vertexBindingDivisor(GLuint bindingIndex, GLuint divisor) +{ + mState.setVertexBindingDivisor(this, bindingIndex, divisor); + mStateCache.onVertexArrayFormatChange(this); +} + +void Context::viewport(GLint x, GLint y, GLsizei width, GLsizei height) +{ + mState.setViewportParams(x, y, width, height); +} + +void Context::vertexAttribIPointer(GLuint index, + GLint size, + VertexAttribType type, + GLsizei stride, + const void *pointer) +{ + mState.setVertexAttribIPointer(this, index, mState.getTargetBuffer(BufferBinding::Array), size, + type, stride, pointer); + mStateCache.onVertexArrayStateChange(this); +} + +void Context::vertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w) +{ + GLint vals[4] = {x, y, z, w}; + mState.setVertexAttribi(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) +{ + GLuint vals[4] = {x, y, z, w}; + mState.setVertexAttribu(index, vals); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttribI4iv(GLuint index, const GLint *v) +{ + mState.setVertexAttribi(index, v); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::vertexAttribI4uiv(GLuint index, const GLuint *v) +{ + mState.setVertexAttribu(index, v); + mStateCache.onDefaultVertexAttributeChange(this); +} + +void Context::getVertexAttribivImpl(GLuint index, GLenum pname, GLint *params) const +{ + const VertexAttribCurrentValueData ¤tValues = + getState().getVertexAttribCurrentValue(index); + const VertexArray *vao = getState().getVertexArray(); + QueryVertexAttribiv(vao->getVertexAttribute(index), vao->getBindingFromAttribIndex(index), + currentValues, pname, params); +} + +void Context::getVertexAttribiv(GLuint index, GLenum pname, GLint *params) +{ + return getVertexAttribivImpl(index, pname, params); +} + +void Context::getVertexAttribivRobust(GLuint index, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getVertexAttribiv(index, pname, params); +} + +void Context::getVertexAttribfv(GLuint index, GLenum pname, GLfloat *params) +{ + const VertexAttribCurrentValueData ¤tValues = + getState().getVertexAttribCurrentValue(index); + const VertexArray *vao = getState().getVertexArray(); + QueryVertexAttribfv(vao->getVertexAttribute(index), vao->getBindingFromAttribIndex(index), + currentValues, pname, params); +} + +void Context::getVertexAttribfvRobust(GLuint index, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + getVertexAttribfv(index, pname, params); +} + +void Context::getVertexAttribIiv(GLuint index, GLenum pname, GLint *params) +{ + const VertexAttribCurrentValueData ¤tValues = + getState().getVertexAttribCurrentValue(index); + const VertexArray *vao = getState().getVertexArray(); + QueryVertexAttribIiv(vao->getVertexAttribute(index), vao->getBindingFromAttribIndex(index), + currentValues, pname, params); +} + +void Context::getVertexAttribIivRobust(GLuint index, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getVertexAttribIiv(index, pname, params); +} + +void Context::getVertexAttribIuiv(GLuint index, GLenum pname, GLuint *params) +{ + const VertexAttribCurrentValueData ¤tValues = + getState().getVertexAttribCurrentValue(index); + const VertexArray *vao = getState().getVertexArray(); + QueryVertexAttribIuiv(vao->getVertexAttribute(index), vao->getBindingFromAttribIndex(index), + currentValues, pname, params); +} + +void Context::getVertexAttribIuivRobust(GLuint index, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + getVertexAttribIuiv(index, pname, params); +} + +void Context::getVertexAttribPointerv(GLuint index, GLenum pname, void **pointer) +{ + const VertexAttribute &attrib = getState().getVertexArray()->getVertexAttribute(index); + QueryVertexAttribPointerv(attrib, pname, pointer); +} + +void Context::getVertexAttribPointervRobust(GLuint index, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + void **pointer) +{ + getVertexAttribPointerv(index, pname, pointer); +} + +void Context::debugMessageControl(GLenum source, + GLenum type, + GLenum severity, + GLsizei count, + const GLuint *ids, + GLboolean enabled) +{ + std::vector<GLuint> idVector(ids, ids + count); + mState.getDebug().setMessageControl(source, type, severity, std::move(idVector), + ConvertToBool(enabled)); +} + +void Context::debugMessageInsert(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *buf) +{ + std::string msg(buf, (length > 0) ? static_cast<size_t>(length) : strlen(buf)); + mState.getDebug().insertMessage(source, type, id, severity, std::move(msg), gl::LOG_INFO, + angle::EntryPoint::GLDebugMessageInsert); +} + +void Context::debugMessageCallback(GLDEBUGPROCKHR callback, const void *userParam) +{ + mState.getDebug().setCallback(callback, userParam); +} + +GLuint Context::getDebugMessageLog(GLuint count, + GLsizei bufSize, + GLenum *sources, + GLenum *types, + GLuint *ids, + GLenum *severities, + GLsizei *lengths, + GLchar *messageLog) +{ + return static_cast<GLuint>(mState.getDebug().getMessages(count, bufSize, sources, types, ids, + severities, lengths, messageLog)); +} + +void Context::pushDebugGroup(GLenum source, GLuint id, GLsizei length, const GLchar *message) +{ + std::string msg(message, (length > 0) ? static_cast<size_t>(length) : strlen(message)); + ANGLE_CONTEXT_TRY(mImplementation->pushDebugGroup(this, source, id, msg)); + mState.getDebug().pushGroup(source, id, std::move(msg)); +} + +angle::Result Context::handleNoopDrawEvent() +{ + return (mImplementation->handleNoopDrawEvent()); +} + +void Context::popDebugGroup() +{ + mState.getDebug().popGroup(); + ANGLE_CONTEXT_TRY(mImplementation->popDebugGroup(this)); +} + +void Context::primitiveBoundingBox(GLfloat minX, + GLfloat minY, + GLfloat minZ, + GLfloat minW, + GLfloat maxX, + GLfloat maxY, + GLfloat maxZ, + GLfloat maxW) +{ + mState.mBoundingBoxMinX = minX; + mState.mBoundingBoxMinY = minY; + mState.mBoundingBoxMinZ = minZ; + mState.mBoundingBoxMinW = minW; + mState.mBoundingBoxMaxX = maxX; + mState.mBoundingBoxMaxY = maxY; + mState.mBoundingBoxMaxZ = maxZ; + mState.mBoundingBoxMaxW = maxW; +} + +void Context::bufferStorage(BufferBinding target, + GLsizeiptr size, + const void *data, + GLbitfield flags) +{ + Buffer *buffer = mState.getTargetBuffer(target); + ASSERT(buffer); + ANGLE_CONTEXT_TRY(buffer->bufferStorage(this, target, size, data, flags)); +} + +void Context::bufferStorageExternal(BufferBinding target, + GLintptr offset, + GLsizeiptr size, + GLeglClientBufferEXT clientBuffer, + GLbitfield flags) +{ + Buffer *buffer = mState.getTargetBuffer(target); + ASSERT(buffer); + + ANGLE_CONTEXT_TRY(buffer->bufferStorageExternal(this, target, size, clientBuffer, flags)); +} + +void Context::namedBufferStorageExternal(GLuint buffer, + GLintptr offset, + GLsizeiptr size, + GLeglClientBufferEXT clientBuffer, + GLbitfield flags) +{ + UNIMPLEMENTED(); +} + +void Context::bufferData(BufferBinding target, GLsizeiptr size, const void *data, BufferUsage usage) +{ + Buffer *buffer = mState.getTargetBuffer(target); + ASSERT(buffer); + ANGLE_CONTEXT_TRY(buffer->bufferData(this, target, data, size, usage)); +} + +void Context::bufferSubData(BufferBinding target, + GLintptr offset, + GLsizeiptr size, + const void *data) +{ + if (data == nullptr || size == 0) + { + return; + } + + Buffer *buffer = mState.getTargetBuffer(target); + ASSERT(buffer); + ANGLE_CONTEXT_TRY(buffer->bufferSubData(this, target, data, size, offset)); +} + +void Context::attachShader(ShaderProgramID program, ShaderProgramID shader) +{ + Program *programObject = mState.mShaderProgramManager->getProgram(program); + Shader *shaderObject = mState.mShaderProgramManager->getShader(shader); + ASSERT(programObject && shaderObject); + programObject->attachShader(shaderObject); +} + +void Context::copyBufferSubData(BufferBinding readTarget, + BufferBinding writeTarget, + GLintptr readOffset, + GLintptr writeOffset, + GLsizeiptr size) +{ + // if size is zero, the copy is a successful no-op + if (size == 0) + { + return; + } + + // TODO(jmadill): cache these. + Buffer *readBuffer = mState.getTargetBuffer(readTarget); + Buffer *writeBuffer = mState.getTargetBuffer(writeTarget); + + ANGLE_CONTEXT_TRY( + writeBuffer->copyBufferSubData(this, readBuffer, readOffset, writeOffset, size)); +} + +void Context::bindAttribLocation(ShaderProgramID program, GLuint index, const GLchar *name) +{ + // Ideally we could share the program query with the validation layer if possible. + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->bindAttributeLocation(index, name); +} + +void Context::bindBufferBase(BufferBinding target, GLuint index, BufferID buffer) +{ + bindBufferRange(target, index, buffer, 0, 0); +} + +void Context::bindBufferRange(BufferBinding target, + GLuint index, + BufferID buffer, + GLintptr offset, + GLsizeiptr size) +{ + Buffer *object = mState.mBufferManager->checkBufferAllocation(mImplementation.get(), buffer); + ANGLE_CONTEXT_TRY(mState.setIndexedBufferBinding(this, target, index, object, offset, size)); + if (target == BufferBinding::Uniform) + { + mUniformBufferObserverBindings[index].bind(object); + mStateCache.onUniformBufferStateChange(this); + } + else if (target == BufferBinding::AtomicCounter) + { + mAtomicCounterBufferObserverBindings[index].bind(object); + mStateCache.onAtomicCounterBufferStateChange(this); + } + else if (target == BufferBinding::ShaderStorage) + { + mShaderStorageBufferObserverBindings[index].bind(object); + mStateCache.onShaderStorageBufferStateChange(this); + } + else + { + mStateCache.onBufferBindingChange(this); + } +} + +void Context::bindFramebuffer(GLenum target, FramebufferID framebuffer) +{ + if (target == GL_READ_FRAMEBUFFER || target == GL_FRAMEBUFFER) + { + bindReadFramebuffer(framebuffer); + } + + if (target == GL_DRAW_FRAMEBUFFER || target == GL_FRAMEBUFFER) + { + bindDrawFramebuffer(framebuffer); + } +} + +void Context::bindRenderbuffer(GLenum target, RenderbufferID renderbuffer) +{ + ASSERT(target == GL_RENDERBUFFER); + Renderbuffer *object = mState.mRenderbufferManager->checkRenderbufferAllocation( + mImplementation.get(), renderbuffer); + mState.setRenderbufferBinding(this, object); +} + +void Context::texStorage2DMultisample(TextureType target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLboolean fixedsamplelocations) +{ + Extents size(width, height, 1); + Texture *texture = getTextureByType(target); + ANGLE_CONTEXT_TRY(texture->setStorageMultisample(this, target, samples, internalformat, size, + ConvertToBool(fixedsamplelocations))); +} + +void Context::texStorage3DMultisample(TextureType target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLboolean fixedsamplelocations) +{ + Extents size(width, height, depth); + Texture *texture = getTextureByType(target); + ANGLE_CONTEXT_TRY(texture->setStorageMultisample(this, target, samples, internalformat, size, + ConvertToBool(fixedsamplelocations))); +} + +void Context::texImage2DExternal(TextureTarget target, + GLint level, + GLint internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type) +{ + Extents size(width, height, 1); + Texture *texture = getTextureByTarget(target); + ANGLE_CONTEXT_TRY( + texture->setImageExternal(this, target, level, internalformat, size, format, type)); +} + +void Context::invalidateTexture(TextureType target) +{ + mImplementation->invalidateTexture(target); + mState.invalidateTextureBindings(target); +} + +void Context::getMultisamplefv(GLenum pname, GLuint index, GLfloat *val) +{ + // According to spec 3.1 Table 20.49: Framebuffer Dependent Values, + // the sample position should be queried by DRAW_FRAMEBUFFER. + ANGLE_CONTEXT_TRY(mState.syncDirtyObject(this, GL_DRAW_FRAMEBUFFER)); + const Framebuffer *framebuffer = mState.getDrawFramebuffer(); + + switch (pname) + { + case GL_SAMPLE_POSITION: + ANGLE_CONTEXT_TRY(framebuffer->getSamplePosition(this, index, val)); + break; + default: + UNREACHABLE(); + } +} + +void Context::getMultisamplefvRobust(GLenum pname, + GLuint index, + GLsizei bufSize, + GLsizei *length, + GLfloat *val) +{ + UNIMPLEMENTED(); +} + +void Context::renderbufferStorage(GLenum target, + GLenum internalformat, + GLsizei width, + GLsizei height) +{ + // Hack for the special WebGL 1 "DEPTH_STENCIL" internal format. + GLenum convertedInternalFormat = getConvertedRenderbufferFormat(internalformat); + + Renderbuffer *renderbuffer = mState.getCurrentRenderbuffer(); + ANGLE_CONTEXT_TRY(renderbuffer->setStorage(this, convertedInternalFormat, width, height)); +} + +void Context::renderbufferStorageMultisample(GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height) +{ + renderbufferStorageMultisampleImpl(target, samples, internalformat, width, height, + MultisamplingMode::Regular); +} + +void Context::renderbufferStorageMultisampleEXT(GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height) +{ + renderbufferStorageMultisampleImpl(target, samples, internalformat, width, height, + MultisamplingMode::MultisampledRenderToTexture); +} + +void Context::renderbufferStorageMultisampleImpl(GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height, + MultisamplingMode mode) +{ + // Hack for the special WebGL 1 "DEPTH_STENCIL" internal format. + GLenum convertedInternalFormat = getConvertedRenderbufferFormat(internalformat); + + Renderbuffer *renderbuffer = mState.getCurrentRenderbuffer(); + ANGLE_CONTEXT_TRY(renderbuffer->setStorageMultisample(this, samples, convertedInternalFormat, + width, height, mode)); +} + +void Context::framebufferTexture2DMultisample(GLenum target, + GLenum attachment, + TextureTarget textarget, + TextureID texture, + GLint level, + GLsizei samples) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture.value != 0) + { + Texture *textureObj = getTexture(texture); + ImageIndex index = ImageIndex::MakeFromTarget(textarget, level, 1); + framebuffer->setAttachmentMultisample(this, GL_TEXTURE, attachment, index, textureObj, + samples); + } + else + { + framebuffer->resetAttachment(this, attachment); + } + + mState.setObjectDirty(target); +} + +void Context::getSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) +{ + const Sync *syncObject = nullptr; + if (!isContextLost()) + { + syncObject = getSync(sync); + } + ANGLE_CONTEXT_TRY(QuerySynciv(this, syncObject, pname, bufSize, length, values)); +} + +void Context::getFramebufferParameteriv(GLenum target, GLenum pname, GLint *params) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + QueryFramebufferParameteriv(framebuffer, pname, params); +} + +void Context::getFramebufferParameterivRobust(GLenum target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::framebufferParameteri(GLenum target, GLenum pname, GLint param) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + SetFramebufferParameteri(this, framebuffer, pname, param); +} + +bool Context::getScratchBuffer(size_t requstedSizeBytes, + angle::MemoryBuffer **scratchBufferOut) const +{ + if (!mScratchBuffer.valid()) + { + mScratchBuffer = mDisplay->requestScratchBuffer(); + } + + ASSERT(mScratchBuffer.valid()); + return mScratchBuffer.value().get(requstedSizeBytes, scratchBufferOut); +} + +angle::ScratchBuffer *Context::getScratchBuffer() const +{ + if (!mScratchBuffer.valid()) + { + mScratchBuffer = mDisplay->requestScratchBuffer(); + } + + ASSERT(mScratchBuffer.valid()); + return &mScratchBuffer.value(); +} + +bool Context::getZeroFilledBuffer(size_t requstedSizeBytes, + angle::MemoryBuffer **zeroBufferOut) const +{ + if (!mZeroFilledBuffer.valid()) + { + mZeroFilledBuffer = mDisplay->requestZeroFilledBuffer(); + } + + ASSERT(mZeroFilledBuffer.valid()); + return mZeroFilledBuffer.value().getInitialized(requstedSizeBytes, zeroBufferOut, 0); +} + +void Context::dispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) +{ + if (numGroupsX == 0u || numGroupsY == 0u || numGroupsZ == 0u) + { + return; + } + + ANGLE_CONTEXT_TRY(prepareForDispatch()); + + angle::Result result = + mImplementation->dispatchCompute(this, numGroupsX, numGroupsY, numGroupsZ); + + // This must be called before convertPpoToComputeOrDraw() so it uses the PPO's compute values + // before convertPpoToComputeOrDraw() reverts the PPO back to graphics. + MarkShaderStorageUsage(this); + + if (ANGLE_UNLIKELY(IsError(result))) + { + return; + } +} + +void Context::dispatchComputeIndirect(GLintptr indirect) +{ + ANGLE_CONTEXT_TRY(prepareForDispatch()); + ANGLE_CONTEXT_TRY(mImplementation->dispatchComputeIndirect(this, indirect)); + + MarkShaderStorageUsage(this); +} + +void Context::texStorage2D(TextureType target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height) +{ + Extents size(width, height, 1); + Texture *texture = getTextureByType(target); + ANGLE_CONTEXT_TRY(texture->setStorage(this, target, levels, internalFormat, size)); +} + +void Context::texStorage3D(TextureType target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLsizei depth) +{ + Extents size(width, height, depth); + Texture *texture = getTextureByType(target); + ANGLE_CONTEXT_TRY(texture->setStorage(this, target, levels, internalFormat, size)); +} + +void Context::memoryBarrier(GLbitfield barriers) +{ + ANGLE_CONTEXT_TRY(mImplementation->memoryBarrier(this, barriers)); +} + +void Context::memoryBarrierByRegion(GLbitfield barriers) +{ + ANGLE_CONTEXT_TRY(mImplementation->memoryBarrierByRegion(this, barriers)); +} + +void Context::multiDrawArrays(PrimitiveMode mode, + const GLint *firsts, + const GLsizei *counts, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->multiDrawArrays(this, mode, firsts, counts, drawcount)); +} + +void Context::multiDrawArraysInstanced(PrimitiveMode mode, + const GLint *firsts, + const GLsizei *counts, + const GLsizei *instanceCounts, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->multiDrawArraysInstanced(this, mode, firsts, counts, + instanceCounts, drawcount)); +} + +void Context::multiDrawArraysIndirect(PrimitiveMode mode, + const void *indirect, + GLsizei drawcount, + GLsizei stride) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->multiDrawArraysIndirect(this, mode, indirect, drawcount, stride)); + MarkShaderStorageUsage(this); +} + +void Context::multiDrawElements(PrimitiveMode mode, + const GLsizei *counts, + DrawElementsType type, + const GLvoid *const *indices, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->multiDrawElements(this, mode, counts, type, indices, drawcount)); +} + +void Context::multiDrawElementsInstanced(PrimitiveMode mode, + const GLsizei *counts, + DrawElementsType type, + const GLvoid *const *indices, + const GLsizei *instanceCounts, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->multiDrawElementsInstanced(this, mode, counts, type, indices, + instanceCounts, drawcount)); +} + +void Context::multiDrawElementsIndirect(PrimitiveMode mode, + DrawElementsType type, + const void *indirect, + GLsizei drawcount, + GLsizei stride) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->multiDrawElementsIndirect(this, mode, type, indirect, drawcount, stride)); + MarkShaderStorageUsage(this); +} + +void Context::drawArraysInstancedBaseInstance(PrimitiveMode mode, + GLint first, + GLsizei count, + GLsizei instanceCount, + GLuint baseInstance) +{ + if (noopDrawInstanced(mode, count, instanceCount)) + { + ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + Program *programObject = mState.getLinkedProgram(this); + + const bool hasBaseInstance = programObject && programObject->hasBaseInstanceUniform(); + if (hasBaseInstance) + { + programObject->setBaseInstanceUniform(baseInstance); + } + + rx::ResetBaseVertexBaseInstance resetUniforms(programObject, false, hasBaseInstance); + + // The input gl_InstanceID does not follow the baseinstance. gl_InstanceID always falls on + // the half-open range [0, instancecount). No need to set other stuff. Except for Vulkan. + + ANGLE_CONTEXT_TRY(mImplementation->drawArraysInstancedBaseInstance( + this, mode, first, count, instanceCount, baseInstance)); + MarkTransformFeedbackBufferUsage(this, count, 1); +} + +void Context::drawArraysInstancedBaseInstanceANGLE(PrimitiveMode mode, + GLint first, + GLsizei count, + GLsizei instanceCount, + GLuint baseInstance) +{ + drawArraysInstancedBaseInstance(mode, first, count, instanceCount, baseInstance); +} + +void Context::drawElementsInstancedBaseInstance(PrimitiveMode mode, + GLsizei count, + DrawElementsType type, + const void *indices, + GLsizei instanceCount, + GLuint baseInstance) +{ + drawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, instanceCount, 0, + baseInstance); +} + +void Context::drawElementsInstancedBaseVertexBaseInstance(PrimitiveMode mode, + GLsizei count, + DrawElementsType type, + const GLvoid *indices, + GLsizei instanceCount, + GLint baseVertex, + GLuint baseInstance) +{ + if (noopDrawInstanced(mode, count, instanceCount)) + { + ANGLE_CONTEXT_TRY(mImplementation->handleNoopDrawEvent()); + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + Program *programObject = mState.getLinkedProgram(this); + + const bool hasBaseVertex = programObject && programObject->hasBaseVertexUniform(); + if (hasBaseVertex) + { + programObject->setBaseVertexUniform(baseVertex); + } + + const bool hasBaseInstance = programObject && programObject->hasBaseInstanceUniform(); + if (hasBaseInstance) + { + programObject->setBaseInstanceUniform(baseInstance); + } + + rx::ResetBaseVertexBaseInstance resetUniforms(programObject, hasBaseVertex, hasBaseInstance); + + ANGLE_CONTEXT_TRY(mImplementation->drawElementsInstancedBaseVertexBaseInstance( + this, mode, count, type, indices, instanceCount, baseVertex, baseInstance)); +} + +void Context::drawElementsInstancedBaseVertexBaseInstanceANGLE(PrimitiveMode mode, + GLsizei count, + DrawElementsType type, + const GLvoid *indices, + GLsizei instanceCount, + GLint baseVertex, + GLuint baseInstance) +{ + drawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, instanceCount, + baseVertex, baseInstance); +} + +void Context::multiDrawArraysInstancedBaseInstance(PrimitiveMode mode, + const GLint *firsts, + const GLsizei *counts, + const GLsizei *instanceCounts, + const GLuint *baseInstances, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->multiDrawArraysInstancedBaseInstance( + this, mode, firsts, counts, instanceCounts, baseInstances, drawcount)); +} + +void Context::multiDrawElementsInstancedBaseVertexBaseInstance(PrimitiveMode mode, + const GLsizei *counts, + DrawElementsType type, + const GLvoid *const *indices, + const GLsizei *instanceCounts, + const GLint *baseVertices, + const GLuint *baseInstances, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->multiDrawElementsInstancedBaseVertexBaseInstance( + this, mode, counts, type, indices, instanceCounts, baseVertices, baseInstances, drawcount)); +} + +void Context::provokingVertex(ProvokingVertexConvention provokeMode) +{ + mState.setProvokingVertex(provokeMode); +} + +GLenum Context::checkFramebufferStatus(GLenum target) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + return framebuffer->checkStatus(this).status; +} + +void Context::compileShader(ShaderProgramID shader) +{ + Shader *shaderObject = GetValidShader(this, angle::EntryPoint::GLCompileShader, shader); + if (!shaderObject) + { + return; + } + shaderObject->compile(this); +} + +void Context::deleteBuffers(GLsizei n, const BufferID *buffers) +{ + for (int i = 0; i < n; i++) + { + deleteBuffer(buffers[i]); + } +} + +void Context::deleteFramebuffers(GLsizei n, const FramebufferID *framebuffers) +{ + for (int i = 0; i < n; i++) + { + if (framebuffers[i].value != 0) + { + deleteFramebuffer(framebuffers[i]); + } + } +} + +void Context::deleteRenderbuffers(GLsizei n, const RenderbufferID *renderbuffers) +{ + for (int i = 0; i < n; i++) + { + deleteRenderbuffer(renderbuffers[i]); + } +} + +void Context::deleteTextures(GLsizei n, const TextureID *textures) +{ + for (int i = 0; i < n; i++) + { + if (textures[i].value != 0) + { + deleteTexture(textures[i]); + } + } +} + +void Context::detachShader(ShaderProgramID program, ShaderProgramID shader) +{ + Program *programObject = getProgramNoResolveLink(program); + ASSERT(programObject); + + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + + programObject->detachShader(this, shaderObject); +} + +void Context::genBuffers(GLsizei n, BufferID *buffers) +{ + for (int i = 0; i < n; i++) + { + buffers[i] = createBuffer(); + } +} + +void Context::genFramebuffers(GLsizei n, FramebufferID *framebuffers) +{ + for (int i = 0; i < n; i++) + { + framebuffers[i] = createFramebuffer(); + } +} + +void Context::genRenderbuffers(GLsizei n, RenderbufferID *renderbuffers) +{ + for (int i = 0; i < n; i++) + { + renderbuffers[i] = createRenderbuffer(); + } +} + +void Context::genTextures(GLsizei n, TextureID *textures) +{ + for (int i = 0; i < n; i++) + { + textures[i] = createTexture(); + } +} + +void Context::getActiveAttrib(ShaderProgramID program, + GLuint index, + GLsizei bufsize, + GLsizei *length, + GLint *size, + GLenum *type, + GLchar *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getActiveAttribute(index, bufsize, length, size, type, name); +} + +void Context::getActiveUniform(ShaderProgramID program, + GLuint index, + GLsizei bufsize, + GLsizei *length, + GLint *size, + GLenum *type, + GLchar *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getActiveUniform(index, bufsize, length, size, type, name); +} + +void Context::getAttachedShaders(ShaderProgramID program, + GLsizei maxcount, + GLsizei *count, + ShaderProgramID *shaders) +{ + Program *programObject = getProgramNoResolveLink(program); + ASSERT(programObject); + programObject->getAttachedShaders(maxcount, count, shaders); +} + +GLint Context::getAttribLocation(ShaderProgramID program, const GLchar *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + return programObject->getAttributeLocation(name); +} + +void Context::getBooleanv(GLenum pname, GLboolean *params) +{ + GLenum nativeType; + unsigned int numParams = 0; + getQueryParameterInfo(pname, &nativeType, &numParams); + + if (nativeType == GL_BOOL) + { + getBooleanvImpl(pname, params); + } + else + { + CastStateValues(this, nativeType, pname, numParams, params); + } +} + +void Context::getBooleanvRobust(GLenum pname, GLsizei bufSize, GLsizei *length, GLboolean *params) +{ + getBooleanv(pname, params); +} + +void Context::getFloatv(GLenum pname, GLfloat *params) +{ + GLenum nativeType; + unsigned int numParams = 0; + getQueryParameterInfo(pname, &nativeType, &numParams); + + if (nativeType == GL_FLOAT) + { + getFloatvImpl(pname, params); + } + else + { + CastStateValues(this, nativeType, pname, numParams, params); + } +} + +void Context::getFloatvRobust(GLenum pname, GLsizei bufSize, GLsizei *length, GLfloat *params) +{ + getFloatv(pname, params); +} + +void Context::getIntegerv(GLenum pname, GLint *params) +{ + GLenum nativeType = GL_NONE; + unsigned int numParams = 0; + getQueryParameterInfo(pname, &nativeType, &numParams); + + if (nativeType == GL_INT) + { + getIntegervImpl(pname, params); + } + else + { + CastStateValues(this, nativeType, pname, numParams, params); + } +} + +void Context::getIntegervRobust(GLenum pname, GLsizei bufSize, GLsizei *length, GLint *data) +{ + getIntegerv(pname, data); +} + +void Context::getProgramiv(ShaderProgramID program, GLenum pname, GLint *params) +{ + // Don't resolve link if checking the link completion status. + Program *programObject = getProgramNoResolveLink(program); + if (!isContextLost() && pname != GL_COMPLETION_STATUS_KHR) + { + programObject = getProgramResolveLink(program); + } + ASSERT(programObject); + QueryProgramiv(this, programObject, pname, params); +} + +void Context::getProgramivRobust(ShaderProgramID program, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getProgramiv(program, pname, params); +} + +void Context::getProgramPipelineiv(ProgramPipelineID pipeline, GLenum pname, GLint *params) +{ + ProgramPipeline *programPipeline = nullptr; + if (!mContextLost) + { + programPipeline = getProgramPipeline(pipeline); + } + QueryProgramPipelineiv(this, programPipeline, pname, params); +} + +MemoryObject *Context::getMemoryObject(MemoryObjectID handle) const +{ + return mState.mMemoryObjectManager->getMemoryObject(handle); +} + +Semaphore *Context::getSemaphore(SemaphoreID handle) const +{ + return mState.mSemaphoreManager->getSemaphore(handle); +} + +void Context::getProgramInfoLog(ShaderProgramID program, + GLsizei bufsize, + GLsizei *length, + GLchar *infolog) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getExecutable().getInfoLog(bufsize, length, infolog); +} + +void Context::getProgramPipelineInfoLog(ProgramPipelineID pipeline, + GLsizei bufSize, + GLsizei *length, + GLchar *infoLog) +{ + ProgramPipeline *programPipeline = getProgramPipeline(pipeline); + if (programPipeline) + { + programPipeline->getExecutable().getInfoLog(bufSize, length, infoLog); + } + else + { + *length = 0; + *infoLog = '\0'; + } +} + +void Context::getShaderiv(ShaderProgramID shader, GLenum pname, GLint *params) +{ + Shader *shaderObject = nullptr; + if (!isContextLost()) + { + shaderObject = getShader(shader); + ASSERT(shaderObject); + } + QueryShaderiv(this, shaderObject, pname, params); +} + +void Context::getShaderivRobust(ShaderProgramID shader, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getShaderiv(shader, pname, params); +} + +void Context::getShaderInfoLog(ShaderProgramID shader, + GLsizei bufsize, + GLsizei *length, + GLchar *infolog) +{ + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + shaderObject->getInfoLog(this, bufsize, length, infolog); +} + +void Context::getShaderPrecisionFormat(GLenum shadertype, + GLenum precisiontype, + GLint *range, + GLint *precision) +{ + // TODO(jmadill): Compute shaders. + + switch (shadertype) + { + case GL_VERTEX_SHADER: + switch (precisiontype) + { + case GL_LOW_FLOAT: + mState.mCaps.vertexLowpFloat.get(range, precision); + break; + case GL_MEDIUM_FLOAT: + mState.mCaps.vertexMediumpFloat.get(range, precision); + break; + case GL_HIGH_FLOAT: + mState.mCaps.vertexHighpFloat.get(range, precision); + break; + + case GL_LOW_INT: + mState.mCaps.vertexLowpInt.get(range, precision); + break; + case GL_MEDIUM_INT: + mState.mCaps.vertexMediumpInt.get(range, precision); + break; + case GL_HIGH_INT: + mState.mCaps.vertexHighpInt.get(range, precision); + break; + + default: + UNREACHABLE(); + return; + } + break; + + case GL_FRAGMENT_SHADER: + switch (precisiontype) + { + case GL_LOW_FLOAT: + mState.mCaps.fragmentLowpFloat.get(range, precision); + break; + case GL_MEDIUM_FLOAT: + mState.mCaps.fragmentMediumpFloat.get(range, precision); + break; + case GL_HIGH_FLOAT: + mState.mCaps.fragmentHighpFloat.get(range, precision); + break; + + case GL_LOW_INT: + mState.mCaps.fragmentLowpInt.get(range, precision); + break; + case GL_MEDIUM_INT: + mState.mCaps.fragmentMediumpInt.get(range, precision); + break; + case GL_HIGH_INT: + mState.mCaps.fragmentHighpInt.get(range, precision); + break; + + default: + UNREACHABLE(); + return; + } + break; + + default: + UNREACHABLE(); + return; + } +} + +void Context::getShaderSource(ShaderProgramID shader, + GLsizei bufsize, + GLsizei *length, + GLchar *source) +{ + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + shaderObject->getSource(bufsize, length, source); +} + +void Context::getUniformfv(ShaderProgramID program, UniformLocation location, GLfloat *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getUniformfv(this, location, params); +} + +void Context::getUniformfvRobust(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + getUniformfv(program, location, params); +} + +void Context::getUniformiv(ShaderProgramID program, UniformLocation location, GLint *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getUniformiv(this, location, params); +} + +void Context::getUniformivRobust(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getUniformiv(program, location, params); +} + +GLint Context::getUniformLocation(ShaderProgramID program, const GLchar *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + return programObject->getUniformLocation(name).value; +} + +GLboolean Context::isBuffer(BufferID buffer) const +{ + if (buffer.value == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getBuffer(buffer)); +} + +GLboolean Context::isEnabled(GLenum cap) const +{ + return mState.getEnableFeature(cap); +} + +GLboolean Context::isEnabledi(GLenum target, GLuint index) const +{ + return mState.getEnableFeatureIndexed(target, index); +} + +GLboolean Context::isFramebuffer(FramebufferID framebuffer) const +{ + if (framebuffer.value == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getFramebuffer(framebuffer)); +} + +GLboolean Context::isProgram(ShaderProgramID program) const +{ + if (program.value == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getProgramNoResolveLink(program)); +} + +GLboolean Context::isRenderbuffer(RenderbufferID renderbuffer) const +{ + if (renderbuffer.value == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getRenderbuffer(renderbuffer)); +} + +GLboolean Context::isShader(ShaderProgramID shader) const +{ + if (shader.value == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getShader(shader)); +} + +GLboolean Context::isTexture(TextureID texture) const +{ + if (texture.value == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getTexture(texture)); +} + +void Context::linkProgram(ShaderProgramID program) +{ + Program *programObject = getProgramNoResolveLink(program); + ASSERT(programObject); + ANGLE_CONTEXT_TRY(programObject->link(this)); + ANGLE_CONTEXT_TRY(onProgramLink(programObject)); +} + +void Context::releaseShaderCompiler() +{ + mCompiler.set(this, nullptr); +} + +void Context::shaderBinary(GLsizei n, + const ShaderProgramID *shaders, + GLenum binaryformat, + const void *binary, + GLsizei length) +{ + // No binary shader formats are supported. + UNIMPLEMENTED(); +} + +void Context::bindFragDataLocationIndexed(ShaderProgramID program, + GLuint colorNumber, + GLuint index, + const char *name) +{ + Program *programObject = getProgramNoResolveLink(program); + programObject->bindFragmentOutputLocation(colorNumber, name); + programObject->bindFragmentOutputIndex(index, name); +} + +void Context::bindFragDataLocation(ShaderProgramID program, GLuint colorNumber, const char *name) +{ + bindFragDataLocationIndexed(program, colorNumber, 0u, name); +} + +int Context::getFragDataIndex(ShaderProgramID program, const char *name) +{ + Program *programObject = getProgramResolveLink(program); + return programObject->getFragDataIndex(name); +} + +int Context::getProgramResourceLocationIndex(ShaderProgramID program, + GLenum programInterface, + const char *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programInterface == GL_PROGRAM_OUTPUT); + return programObject->getFragDataIndex(name); +} + +void Context::shaderSource(ShaderProgramID shader, + GLsizei count, + const GLchar *const *string, + const GLint *length) +{ + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + shaderObject->setSource(count, string, length); +} + +void Context::stencilFunc(GLenum func, GLint ref, GLuint mask) +{ + stencilFuncSeparate(GL_FRONT_AND_BACK, func, ref, mask); +} + +void Context::stencilMask(GLuint mask) +{ + stencilMaskSeparate(GL_FRONT_AND_BACK, mask); +} + +void Context::stencilOp(GLenum fail, GLenum zfail, GLenum zpass) +{ + stencilOpSeparate(GL_FRONT_AND_BACK, fail, zfail, zpass); +} + +void Context::patchParameteri(GLenum pname, GLint value) +{ + switch (pname) + { + case GL_PATCH_VERTICES: + mState.setPatchVertices(value); + break; + default: + break; + } +} + +Program *Context::getActiveLinkedProgram() const +{ + Program *program = mState.getLinkedProgram(this); + if (!program) + { + ProgramPipeline *programPipelineObject = mState.getProgramPipeline(); + if (programPipelineObject) + { + program = programPipelineObject->getLinkedActiveShaderProgram(this); + } + } + + return program; +} + +void Context::uniform1f(UniformLocation location, GLfloat x) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform1fv(location, 1, &x); +} + +void Context::uniform1fv(UniformLocation location, GLsizei count, const GLfloat *v) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform1fv(location, count, v); +} + +void Context::setUniform1iImpl(Program *program, + UniformLocation location, + GLsizei count, + const GLint *v) +{ + program->setUniform1iv(this, location, count, v); +} + +void Context::onSamplerUniformChange(size_t textureUnitIndex) +{ + mState.onActiveTextureChange(this, textureUnitIndex); + mStateCache.onActiveTextureChange(this); +} + +void Context::uniform1i(UniformLocation location, GLint x) +{ + Program *program = getActiveLinkedProgram(); + setUniform1iImpl(program, location, 1, &x); +} + +void Context::uniform1iv(UniformLocation location, GLsizei count, const GLint *v) +{ + Program *program = getActiveLinkedProgram(); + setUniform1iImpl(program, location, count, v); +} + +void Context::uniform2f(UniformLocation location, GLfloat x, GLfloat y) +{ + GLfloat xy[2] = {x, y}; + Program *program = getActiveLinkedProgram(); + program->setUniform2fv(location, 1, xy); +} + +void Context::uniform2fv(UniformLocation location, GLsizei count, const GLfloat *v) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform2fv(location, count, v); +} + +void Context::uniform2i(UniformLocation location, GLint x, GLint y) +{ + GLint xy[2] = {x, y}; + Program *program = getActiveLinkedProgram(); + program->setUniform2iv(location, 1, xy); +} + +void Context::uniform2iv(UniformLocation location, GLsizei count, const GLint *v) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform2iv(location, count, v); +} + +void Context::uniform3f(UniformLocation location, GLfloat x, GLfloat y, GLfloat z) +{ + GLfloat xyz[3] = {x, y, z}; + Program *program = getActiveLinkedProgram(); + program->setUniform3fv(location, 1, xyz); +} + +void Context::uniform3fv(UniformLocation location, GLsizei count, const GLfloat *v) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform3fv(location, count, v); +} + +void Context::uniform3i(UniformLocation location, GLint x, GLint y, GLint z) +{ + GLint xyz[3] = {x, y, z}; + Program *program = getActiveLinkedProgram(); + program->setUniform3iv(location, 1, xyz); +} + +void Context::uniform3iv(UniformLocation location, GLsizei count, const GLint *v) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform3iv(location, count, v); +} + +void Context::uniform4f(UniformLocation location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + GLfloat xyzw[4] = {x, y, z, w}; + Program *program = getActiveLinkedProgram(); + program->setUniform4fv(location, 1, xyzw); +} + +void Context::uniform4fv(UniformLocation location, GLsizei count, const GLfloat *v) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform4fv(location, count, v); +} + +void Context::uniform4i(UniformLocation location, GLint x, GLint y, GLint z, GLint w) +{ + GLint xyzw[4] = {x, y, z, w}; + Program *program = getActiveLinkedProgram(); + program->setUniform4iv(location, 1, xyzw); +} + +void Context::uniform4iv(UniformLocation location, GLsizei count, const GLint *v) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform4iv(location, count, v); +} + +void Context::uniformMatrix2fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix2fv(location, count, transpose, value); +} + +void Context::uniformMatrix3fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix3fv(location, count, transpose, value); +} + +void Context::uniformMatrix4fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix4fv(location, count, transpose, value); +} + +void Context::validateProgram(ShaderProgramID program) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->validate(mState.mCaps); +} + +void Context::validateProgramPipeline(ProgramPipelineID pipeline) +{ + // GLES spec 3.2, Section 7.4 "Program Pipeline Objects" + // If pipeline is a name that has been generated (without subsequent deletion) by + // GenProgramPipelines, but refers to a program pipeline object that has not been + // previously bound, the GL first creates a new state vector in the same manner as + // when BindProgramPipeline creates a new program pipeline object. + // + // void BindProgramPipeline( uint pipeline ); + // pipeline is the program pipeline object name. The resulting program pipeline + // object is a new state vector, comprising all the state and with the same initial values + // listed in table 21.20. + // + // If we do not have a pipeline object that's been created with glBindProgramPipeline, we leave + // VALIDATE_STATUS at it's default false value without generating a pipeline object. + if (!getProgramPipeline(pipeline)) + { + return; + } + + ProgramPipeline *programPipeline = + mState.mProgramPipelineManager->checkProgramPipelineAllocation(mImplementation.get(), + pipeline); + ASSERT(programPipeline); + + programPipeline->validate(this); +} + +void Context::getProgramBinary(ShaderProgramID program, + GLsizei bufSize, + GLsizei *length, + GLenum *binaryFormat, + void *binary) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject != nullptr); + + ANGLE_CONTEXT_TRY(programObject->saveBinary(this, binaryFormat, binary, bufSize, length)); +} + +void Context::programBinary(ShaderProgramID program, + GLenum binaryFormat, + const void *binary, + GLsizei length) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject != nullptr); + + ANGLE_CONTEXT_TRY(programObject->loadBinary(this, binaryFormat, binary, length)); + ANGLE_CONTEXT_TRY(onProgramLink(programObject)); +} + +void Context::uniform1ui(UniformLocation location, GLuint v0) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform1uiv(location, 1, &v0); +} + +void Context::uniform2ui(UniformLocation location, GLuint v0, GLuint v1) +{ + Program *program = getActiveLinkedProgram(); + const GLuint xy[] = {v0, v1}; + program->setUniform2uiv(location, 1, xy); +} + +void Context::uniform3ui(UniformLocation location, GLuint v0, GLuint v1, GLuint v2) +{ + Program *program = getActiveLinkedProgram(); + const GLuint xyz[] = {v0, v1, v2}; + program->setUniform3uiv(location, 1, xyz); +} + +void Context::uniform4ui(UniformLocation location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) +{ + Program *program = getActiveLinkedProgram(); + const GLuint xyzw[] = {v0, v1, v2, v3}; + program->setUniform4uiv(location, 1, xyzw); +} + +void Context::uniform1uiv(UniformLocation location, GLsizei count, const GLuint *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform1uiv(location, count, value); +} +void Context::uniform2uiv(UniformLocation location, GLsizei count, const GLuint *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform2uiv(location, count, value); +} + +void Context::uniform3uiv(UniformLocation location, GLsizei count, const GLuint *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform3uiv(location, count, value); +} + +void Context::uniform4uiv(UniformLocation location, GLsizei count, const GLuint *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniform4uiv(location, count, value); +} + +void Context::genQueries(GLsizei n, QueryID *ids) +{ + for (GLsizei i = 0; i < n; i++) + { + QueryID handle = QueryID{mQueryHandleAllocator.allocate()}; + mQueryMap.assign(handle, nullptr); + ids[i] = handle; + } +} + +void Context::deleteQueries(GLsizei n, const QueryID *ids) +{ + for (int i = 0; i < n; i++) + { + QueryID query = ids[i]; + + Query *queryObject = nullptr; + if (mQueryMap.erase(query, &queryObject)) + { + mQueryHandleAllocator.release(query.value); + if (queryObject) + { + queryObject->release(this); + } + } + } +} + +bool Context::isQueryGenerated(QueryID query) const +{ + return mQueryMap.contains(query); +} + +GLboolean Context::isQuery(QueryID id) const +{ + return ConvertToGLBoolean(getQuery(id) != nullptr); +} + +void Context::uniformMatrix2x3fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix2x3fv(location, count, transpose, value); +} + +void Context::uniformMatrix3x2fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix3x2fv(location, count, transpose, value); +} + +void Context::uniformMatrix2x4fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix2x4fv(location, count, transpose, value); +} + +void Context::uniformMatrix4x2fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix4x2fv(location, count, transpose, value); +} + +void Context::uniformMatrix3x4fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix3x4fv(location, count, transpose, value); +} + +void Context::uniformMatrix4x3fv(UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = getActiveLinkedProgram(); + program->setUniformMatrix4x3fv(location, count, transpose, value); +} + +void Context::deleteVertexArrays(GLsizei n, const VertexArrayID *arrays) +{ + for (int arrayIndex = 0; arrayIndex < n; arrayIndex++) + { + VertexArrayID vertexArray = arrays[arrayIndex]; + + if (arrays[arrayIndex].value != 0) + { + VertexArray *vertexArrayObject = nullptr; + if (mVertexArrayMap.erase(vertexArray, &vertexArrayObject)) + { + if (vertexArrayObject != nullptr) + { + detachVertexArray(vertexArray); + vertexArrayObject->onDestroy(this); + } + + mVertexArrayHandleAllocator.release(vertexArray.value); + } + } + } +} + +void Context::genVertexArrays(GLsizei n, VertexArrayID *arrays) +{ + for (int arrayIndex = 0; arrayIndex < n; arrayIndex++) + { + VertexArrayID vertexArray = {mVertexArrayHandleAllocator.allocate()}; + mVertexArrayMap.assign(vertexArray, nullptr); + arrays[arrayIndex] = vertexArray; + } +} + +GLboolean Context::isVertexArray(VertexArrayID array) const +{ + if (array.value == 0) + { + return GL_FALSE; + } + + VertexArray *vao = getVertexArray(array); + return ConvertToGLBoolean(vao != nullptr); +} + +void Context::endTransformFeedback() +{ + TransformFeedback *transformFeedback = mState.getCurrentTransformFeedback(); + ANGLE_CONTEXT_TRY(transformFeedback->end(this)); + mStateCache.onActiveTransformFeedbackChange(this); +} + +void Context::transformFeedbackVaryings(ShaderProgramID program, + GLsizei count, + const GLchar *const *varyings, + GLenum bufferMode) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setTransformFeedbackVaryings(count, varyings, bufferMode); +} + +void Context::getTransformFeedbackVarying(ShaderProgramID program, + GLuint index, + GLsizei bufSize, + GLsizei *length, + GLsizei *size, + GLenum *type, + GLchar *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getTransformFeedbackVarying(index, bufSize, length, size, type, name); +} + +void Context::deleteTransformFeedbacks(GLsizei n, const TransformFeedbackID *ids) +{ + for (int i = 0; i < n; i++) + { + TransformFeedbackID transformFeedback = ids[i]; + if (transformFeedback.value == 0) + { + continue; + } + + TransformFeedback *transformFeedbackObject = nullptr; + if (mTransformFeedbackMap.erase(transformFeedback, &transformFeedbackObject)) + { + if (transformFeedbackObject != nullptr) + { + detachTransformFeedback(transformFeedback); + transformFeedbackObject->release(this); + } + + mTransformFeedbackHandleAllocator.release(transformFeedback.value); + } + } +} + +void Context::genTransformFeedbacks(GLsizei n, TransformFeedbackID *ids) +{ + for (int i = 0; i < n; i++) + { + TransformFeedbackID transformFeedback = {mTransformFeedbackHandleAllocator.allocate()}; + mTransformFeedbackMap.assign(transformFeedback, nullptr); + ids[i] = transformFeedback; + } +} + +GLboolean Context::isTransformFeedback(TransformFeedbackID id) const +{ + if (id.value == 0) + { + // The 3.0.4 spec [section 6.1.11] states that if ID is zero, IsTransformFeedback + // returns FALSE + return GL_FALSE; + } + + const TransformFeedback *transformFeedback = getTransformFeedback(id); + return ConvertToGLBoolean(transformFeedback != nullptr); +} + +void Context::pauseTransformFeedback() +{ + TransformFeedback *transformFeedback = mState.getCurrentTransformFeedback(); + ANGLE_CONTEXT_TRY(transformFeedback->pause(this)); + mStateCache.onActiveTransformFeedbackChange(this); +} + +void Context::resumeTransformFeedback() +{ + TransformFeedback *transformFeedback = mState.getCurrentTransformFeedback(); + ANGLE_CONTEXT_TRY(transformFeedback->resume(this)); + mStateCache.onActiveTransformFeedbackChange(this); +} + +void Context::getUniformuiv(ShaderProgramID program, UniformLocation location, GLuint *params) +{ + const Program *programObject = getProgramResolveLink(program); + programObject->getUniformuiv(this, location, params); +} + +void Context::getUniformuivRobust(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + getUniformuiv(program, location, params); +} + +GLint Context::getFragDataLocation(ShaderProgramID program, const GLchar *name) +{ + const Program *programObject = getProgramResolveLink(program); + return programObject->getFragDataLocation(name); +} + +void Context::getUniformIndices(ShaderProgramID program, + GLsizei uniformCount, + const GLchar *const *uniformNames, + GLuint *uniformIndices) +{ + const Program *programObject = getProgramResolveLink(program); + if (!programObject->isLinked()) + { + for (int uniformId = 0; uniformId < uniformCount; uniformId++) + { + uniformIndices[uniformId] = GL_INVALID_INDEX; + } + } + else + { + for (int uniformId = 0; uniformId < uniformCount; uniformId++) + { + uniformIndices[uniformId] = programObject->getUniformIndex(uniformNames[uniformId]); + } + } +} + +void Context::getActiveUniformsiv(ShaderProgramID program, + GLsizei uniformCount, + const GLuint *uniformIndices, + GLenum pname, + GLint *params) +{ + const Program *programObject = getProgramResolveLink(program); + for (int uniformId = 0; uniformId < uniformCount; uniformId++) + { + const GLuint index = uniformIndices[uniformId]; + params[uniformId] = GetUniformResourceProperty(programObject, index, pname); + } +} + +GLuint Context::getUniformBlockIndex(ShaderProgramID program, const GLchar *uniformBlockName) +{ + const Program *programObject = getProgramResolveLink(program); + return programObject->getUniformBlockIndex(uniformBlockName); +} + +void Context::getActiveUniformBlockiv(ShaderProgramID program, + UniformBlockIndex uniformBlockIndex, + GLenum pname, + GLint *params) +{ + const Program *programObject = getProgramResolveLink(program); + QueryActiveUniformBlockiv(programObject, uniformBlockIndex, pname, params); +} + +void Context::getActiveUniformBlockivRobust(ShaderProgramID program, + UniformBlockIndex uniformBlockIndex, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getActiveUniformBlockiv(program, uniformBlockIndex, pname, params); +} + +void Context::getActiveUniformBlockName(ShaderProgramID program, + UniformBlockIndex uniformBlockIndex, + GLsizei bufSize, + GLsizei *length, + GLchar *uniformBlockName) +{ + const Program *programObject = getProgramResolveLink(program); + programObject->getActiveUniformBlockName(this, uniformBlockIndex, bufSize, length, + uniformBlockName); +} + +void Context::uniformBlockBinding(ShaderProgramID program, + UniformBlockIndex uniformBlockIndex, + GLuint uniformBlockBinding) +{ + Program *programObject = getProgramResolveLink(program); + programObject->bindUniformBlock(uniformBlockIndex, uniformBlockBinding); + + // Note: If the Program is shared between Contexts we would be better using Observer/Subject. + if (programObject->isInUse()) + { + mState.setObjectDirty(GL_PROGRAM); + mStateCache.onUniformBufferStateChange(this); + } +} + +GLsync Context::fenceSync(GLenum condition, GLbitfield flags) +{ + GLuint handle = mState.mSyncManager->createSync(mImplementation.get()); + GLsync syncHandle = reinterpret_cast<GLsync>(static_cast<uintptr_t>(handle)); + + Sync *syncObject = getSync(syncHandle); + if (syncObject->set(this, condition, flags) == angle::Result::Stop) + { + deleteSync(syncHandle); + return nullptr; + } + + return syncHandle; +} + +GLboolean Context::isSync(GLsync sync) const +{ + return (getSync(sync) != nullptr); +} + +GLenum Context::clientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) +{ + Sync *syncObject = getSync(sync); + + GLenum result = GL_WAIT_FAILED; + if (syncObject->clientWait(this, flags, timeout, &result) == angle::Result::Stop) + { + return GL_WAIT_FAILED; + } + return result; +} + +void Context::waitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) +{ + Sync *syncObject = getSync(sync); + ANGLE_CONTEXT_TRY(syncObject->serverWait(this, flags, timeout)); +} + +void Context::getInteger64v(GLenum pname, GLint64 *params) +{ + GLenum nativeType = GL_NONE; + unsigned int numParams = 0; + getQueryParameterInfo(pname, &nativeType, &numParams); + + if (nativeType == GL_INT_64_ANGLEX) + { + getInteger64vImpl(pname, params); + } + else + { + CastStateValues(this, nativeType, pname, numParams, params); + } +} + +void Context::getInteger64vRobust(GLenum pname, GLsizei bufSize, GLsizei *length, GLint64 *data) +{ + getInteger64v(pname, data); +} + +void Context::getBufferParameteri64v(BufferBinding target, GLenum pname, GLint64 *params) +{ + Buffer *buffer = mState.getTargetBuffer(target); + QueryBufferParameteri64v(buffer, pname, params); +} + +void Context::getBufferParameteri64vRobust(BufferBinding target, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint64 *params) +{ + getBufferParameteri64v(target, pname, params); +} + +void Context::genSamplers(GLsizei count, SamplerID *samplers) +{ + for (int i = 0; i < count; i++) + { + samplers[i] = mState.mSamplerManager->createSampler(); + } +} + +void Context::deleteSamplers(GLsizei count, const SamplerID *samplers) +{ + for (int i = 0; i < count; i++) + { + SamplerID sampler = samplers[i]; + + if (mState.mSamplerManager->getSampler(sampler)) + { + detachSampler(sampler); + } + + mState.mSamplerManager->deleteObject(this, sampler); + } +} + +void Context::minSampleShading(GLfloat value) +{ + mState.setMinSampleShading(value); +} + +void Context::getInternalformativ(GLenum target, + GLenum internalformat, + GLenum pname, + GLsizei bufSize, + GLint *params) +{ + const TextureCaps &formatCaps = mState.mTextureCaps.get(internalformat); + QueryInternalFormativ(formatCaps, pname, bufSize, params); +} + +void Context::getInternalformativRobust(GLenum target, + GLenum internalformat, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getInternalformativ(target, internalformat, pname, bufSize, params); +} + +void Context::programUniform1i(ShaderProgramID program, UniformLocation location, GLint v0) +{ + programUniform1iv(program, location, 1, &v0); +} + +void Context::programUniform2i(ShaderProgramID program, + UniformLocation location, + GLint v0, + GLint v1) +{ + GLint xy[2] = {v0, v1}; + programUniform2iv(program, location, 1, xy); +} + +void Context::programUniform3i(ShaderProgramID program, + UniformLocation location, + GLint v0, + GLint v1, + GLint v2) +{ + GLint xyz[3] = {v0, v1, v2}; + programUniform3iv(program, location, 1, xyz); +} + +void Context::programUniform4i(ShaderProgramID program, + UniformLocation location, + GLint v0, + GLint v1, + GLint v2, + GLint v3) +{ + GLint xyzw[4] = {v0, v1, v2, v3}; + programUniform4iv(program, location, 1, xyzw); +} + +void Context::programUniform1ui(ShaderProgramID program, UniformLocation location, GLuint v0) +{ + programUniform1uiv(program, location, 1, &v0); +} + +void Context::programUniform2ui(ShaderProgramID program, + UniformLocation location, + GLuint v0, + GLuint v1) +{ + GLuint xy[2] = {v0, v1}; + programUniform2uiv(program, location, 1, xy); +} + +void Context::programUniform3ui(ShaderProgramID program, + UniformLocation location, + GLuint v0, + GLuint v1, + GLuint v2) +{ + GLuint xyz[3] = {v0, v1, v2}; + programUniform3uiv(program, location, 1, xyz); +} + +void Context::programUniform4ui(ShaderProgramID program, + UniformLocation location, + GLuint v0, + GLuint v1, + GLuint v2, + GLuint v3) +{ + GLuint xyzw[4] = {v0, v1, v2, v3}; + programUniform4uiv(program, location, 1, xyzw); +} + +void Context::programUniform1f(ShaderProgramID program, UniformLocation location, GLfloat v0) +{ + programUniform1fv(program, location, 1, &v0); +} + +void Context::programUniform2f(ShaderProgramID program, + UniformLocation location, + GLfloat v0, + GLfloat v1) +{ + GLfloat xy[2] = {v0, v1}; + programUniform2fv(program, location, 1, xy); +} + +void Context::programUniform3f(ShaderProgramID program, + UniformLocation location, + GLfloat v0, + GLfloat v1, + GLfloat v2) +{ + GLfloat xyz[3] = {v0, v1, v2}; + programUniform3fv(program, location, 1, xyz); +} + +void Context::programUniform4f(ShaderProgramID program, + UniformLocation location, + GLfloat v0, + GLfloat v1, + GLfloat v2, + GLfloat v3) +{ + GLfloat xyzw[4] = {v0, v1, v2, v3}; + programUniform4fv(program, location, 1, xyzw); +} + +void Context::programUniform1iv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + setUniform1iImpl(programObject, location, count, value); +} + +void Context::programUniform2iv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform2iv(location, count, value); +} + +void Context::programUniform3iv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform3iv(location, count, value); +} + +void Context::programUniform4iv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform4iv(location, count, value); +} + +void Context::programUniform1uiv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLuint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform1uiv(location, count, value); +} + +void Context::programUniform2uiv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLuint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform2uiv(location, count, value); +} + +void Context::programUniform3uiv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLuint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform3uiv(location, count, value); +} + +void Context::programUniform4uiv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLuint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform4uiv(location, count, value); +} + +void Context::programUniform1fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform1fv(location, count, value); +} + +void Context::programUniform2fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform2fv(location, count, value); +} + +void Context::programUniform3fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform3fv(location, count, value); +} + +void Context::programUniform4fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform4fv(location, count, value); +} + +void Context::programUniformMatrix2fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix2fv(location, count, transpose, value); +} + +void Context::programUniformMatrix3fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix3fv(location, count, transpose, value); +} + +void Context::programUniformMatrix4fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix4fv(location, count, transpose, value); +} + +void Context::programUniformMatrix2x3fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix2x3fv(location, count, transpose, value); +} + +void Context::programUniformMatrix3x2fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix3x2fv(location, count, transpose, value); +} + +void Context::programUniformMatrix2x4fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix2x4fv(location, count, transpose, value); +} + +void Context::programUniformMatrix4x2fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix4x2fv(location, count, transpose, value); +} + +void Context::programUniformMatrix3x4fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix3x4fv(location, count, transpose, value); +} + +void Context::programUniformMatrix4x3fv(ShaderProgramID program, + UniformLocation location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix4x3fv(location, count, transpose, value); +} + +bool Context::isCurrentTransformFeedback(const TransformFeedback *tf) const +{ + return mState.isCurrentTransformFeedback(tf); +} + +void Context::genProgramPipelines(GLsizei count, ProgramPipelineID *pipelines) +{ + for (int i = 0; i < count; i++) + { + pipelines[i] = createProgramPipeline(); + } +} + +void Context::deleteProgramPipelines(GLsizei count, const ProgramPipelineID *pipelines) +{ + for (int i = 0; i < count; i++) + { + if (pipelines[i].value != 0) + { + deleteProgramPipeline(pipelines[i]); + } + } +} + +GLboolean Context::isProgramPipeline(ProgramPipelineID pipeline) const +{ + if (pipeline.value == 0) + { + return GL_FALSE; + } + + if (getProgramPipeline(pipeline)) + { + return GL_TRUE; + } + + return GL_FALSE; +} + +void Context::finishFenceNV(FenceNVID fence) +{ + FenceNV *fenceObject = getFenceNV(fence); + + ASSERT(fenceObject && fenceObject->isSet()); + ANGLE_CONTEXT_TRY(fenceObject->finish(this)); +} + +void Context::getFenceivNV(FenceNVID fence, GLenum pname, GLint *params) +{ + FenceNV *fenceObject = getFenceNV(fence); + + ASSERT(fenceObject && fenceObject->isSet()); + + switch (pname) + { + case GL_FENCE_STATUS_NV: + { + // GL_NV_fence spec: + // Once the status of a fence has been finished (via FinishFenceNV) or tested and + // the returned status is TRUE (via either TestFenceNV or GetFenceivNV querying the + // FENCE_STATUS_NV), the status remains TRUE until the next SetFenceNV of the fence. + GLboolean status = GL_TRUE; + if (fenceObject->getStatus() != GL_TRUE) + { + ANGLE_CONTEXT_TRY(fenceObject->test(this, &status)); + } + *params = status; + break; + } + + case GL_FENCE_CONDITION_NV: + { + *params = static_cast<GLint>(fenceObject->getCondition()); + break; + } + + default: + UNREACHABLE(); + } +} + +void Context::getTranslatedShaderSource(ShaderProgramID shader, + GLsizei bufsize, + GLsizei *length, + GLchar *source) +{ + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + shaderObject->getTranslatedSourceWithDebugInfo(this, bufsize, length, source); +} + +void Context::getnUniformfv(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLfloat *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + + programObject->getUniformfv(this, location, params); +} + +void Context::getnUniformfvRobust(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + UNIMPLEMENTED(); +} + +void Context::getnUniformiv(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLint *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + + programObject->getUniformiv(this, location, params); +} + +void Context::getnUniformuiv(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLuint *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + + programObject->getUniformuiv(this, location, params); +} + +void Context::getnUniformivRobust(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getnUniformuivRobust(ShaderProgramID program, + UniformLocation location, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + UNIMPLEMENTED(); +} + +GLboolean Context::isFenceNV(FenceNVID fence) const +{ + FenceNV *fenceObject = getFenceNV(fence); + + if (fenceObject == nullptr) + { + return GL_FALSE; + } + + // GL_NV_fence spec: + // A name returned by GenFencesNV, but not yet set via SetFenceNV, is not the name of an + // existing fence. + return fenceObject->isSet(); +} + +void Context::readnPixels(GLint x, + GLint y, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + GLsizei bufSize, + void *data) +{ + return readPixels(x, y, width, height, format, type, data); +} + +void Context::setFenceNV(FenceNVID fence, GLenum condition) +{ + ASSERT(condition == GL_ALL_COMPLETED_NV); + + FenceNV *fenceObject = getFenceNV(fence); + ASSERT(fenceObject != nullptr); + ANGLE_CONTEXT_TRY(fenceObject->set(this, condition)); +} + +GLboolean Context::testFenceNV(FenceNVID fence) +{ + FenceNV *fenceObject = getFenceNV(fence); + + ASSERT(fenceObject != nullptr); + ASSERT(fenceObject->isSet() == GL_TRUE); + + GLboolean result = GL_TRUE; + if (fenceObject->test(this, &result) == angle::Result::Stop) + { + return GL_TRUE; + } + + return result; +} + +void Context::deleteMemoryObjects(GLsizei n, const MemoryObjectID *memoryObjects) +{ + for (int i = 0; i < n; i++) + { + deleteMemoryObject(memoryObjects[i]); + } +} + +GLboolean Context::isMemoryObject(MemoryObjectID memoryObject) const +{ + if (memoryObject.value == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getMemoryObject(memoryObject)); +} + +void Context::createMemoryObjects(GLsizei n, MemoryObjectID *memoryObjects) +{ + for (int i = 0; i < n; i++) + { + memoryObjects[i] = createMemoryObject(); + } +} + +void Context::memoryObjectParameteriv(MemoryObjectID memory, GLenum pname, const GLint *params) +{ + MemoryObject *memoryObject = getMemoryObject(memory); + ASSERT(memoryObject); + ANGLE_CONTEXT_TRY(SetMemoryObjectParameteriv(this, memoryObject, pname, params)); +} + +void Context::getMemoryObjectParameteriv(MemoryObjectID memory, GLenum pname, GLint *params) +{ + const MemoryObject *memoryObject = getMemoryObject(memory); + ASSERT(memoryObject); + QueryMemoryObjectParameteriv(memoryObject, pname, params); +} + +void Context::texStorageMem2D(TextureType target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height, + MemoryObjectID memory, + GLuint64 offset) +{ + texStorageMemFlags2D(target, levels, internalFormat, width, height, memory, offset, 0, + std::numeric_limits<uint32_t>::max(), nullptr); +} + +void Context::texStorageMem2DMultisample(TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLboolean fixedSampleLocations, + MemoryObjectID memory, + GLuint64 offset) +{ + UNIMPLEMENTED(); +} + +void Context::texStorageMem3D(TextureType target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLsizei depth, + MemoryObjectID memory, + GLuint64 offset) +{ + UNIMPLEMENTED(); +} + +void Context::texStorageMem3DMultisample(TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLboolean fixedSampleLocations, + MemoryObjectID memory, + GLuint64 offset) +{ + UNIMPLEMENTED(); +} + +void Context::bufferStorageMem(TextureType target, + GLsizeiptr size, + MemoryObjectID memory, + GLuint64 offset) +{ + UNIMPLEMENTED(); +} + +void Context::importMemoryFd(MemoryObjectID memory, GLuint64 size, HandleType handleType, GLint fd) +{ + MemoryObject *memoryObject = getMemoryObject(memory); + ASSERT(memoryObject != nullptr); + ANGLE_CONTEXT_TRY(memoryObject->importFd(this, size, handleType, fd)); +} + +void Context::texStorageMemFlags2D(TextureType target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height, + MemoryObjectID memory, + GLuint64 offset, + GLbitfield createFlags, + GLbitfield usageFlags, + const void *imageCreateInfoPNext) +{ + MemoryObject *memoryObject = getMemoryObject(memory); + ASSERT(memoryObject); + Extents size(width, height, 1); + Texture *texture = getTextureByType(target); + ANGLE_CONTEXT_TRY(texture->setStorageExternalMemory(this, target, levels, internalFormat, size, + memoryObject, offset, createFlags, + usageFlags, imageCreateInfoPNext)); +} + +void Context::texStorageMemFlags2DMultisample(TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLboolean fixedSampleLocations, + MemoryObjectID memory, + GLuint64 offset, + GLbitfield createFlags, + GLbitfield usageFlags, + const void *imageCreateInfoPNext) +{ + UNIMPLEMENTED(); +} + +void Context::texStorageMemFlags3D(TextureType target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLsizei depth, + MemoryObjectID memory, + GLuint64 offset, + GLbitfield createFlags, + GLbitfield usageFlags, + const void *imageCreateInfoPNext) +{ + UNIMPLEMENTED(); +} + +void Context::texStorageMemFlags3DMultisample(TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLboolean fixedSampleLocations, + MemoryObjectID memory, + GLuint64 offset, + GLbitfield createFlags, + GLbitfield usageFlags, + const void *imageCreateInfoPNext) +{ + UNIMPLEMENTED(); +} + +void Context::importMemoryZirconHandle(MemoryObjectID memory, + GLuint64 size, + HandleType handleType, + GLuint handle) +{ + MemoryObject *memoryObject = getMemoryObject(memory); + ASSERT(memoryObject != nullptr); + ANGLE_CONTEXT_TRY(memoryObject->importZirconHandle(this, size, handleType, handle)); +} + +void Context::genSemaphores(GLsizei n, SemaphoreID *semaphores) +{ + for (int i = 0; i < n; i++) + { + semaphores[i] = createSemaphore(); + } +} + +void Context::deleteSemaphores(GLsizei n, const SemaphoreID *semaphores) +{ + for (int i = 0; i < n; i++) + { + deleteSemaphore(semaphores[i]); + } +} + +GLboolean Context::isSemaphore(SemaphoreID semaphore) const +{ + if (semaphore.value == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getSemaphore(semaphore)); +} + +void Context::semaphoreParameterui64v(SemaphoreID semaphore, GLenum pname, const GLuint64 *params) +{ + UNIMPLEMENTED(); +} + +void Context::getSemaphoreParameterui64v(SemaphoreID semaphore, GLenum pname, GLuint64 *params) +{ + UNIMPLEMENTED(); +} + +void Context::acquireTextures(GLuint numTextures, + const TextureID *textureIds, + const GLenum *layouts) +{ + TextureBarrierVector textureBarriers(numTextures); + for (size_t i = 0; i < numTextures; i++) + { + textureBarriers[i].texture = getTexture(textureIds[i]); + textureBarriers[i].layout = layouts[i]; + } + ANGLE_CONTEXT_TRY(mImplementation->acquireTextures(this, textureBarriers)); +} + +void Context::releaseTextures(GLuint numTextures, const TextureID *textureIds, GLenum *layouts) +{ + TextureBarrierVector textureBarriers(numTextures); + for (size_t i = 0; i < numTextures; i++) + { + textureBarriers[i].texture = getTexture(textureIds[i]); + } + ANGLE_CONTEXT_TRY(mImplementation->releaseTextures(this, &textureBarriers)); + for (size_t i = 0; i < numTextures; i++) + { + layouts[i] = textureBarriers[i].layout; + } +} + +void Context::waitSemaphore(SemaphoreID semaphoreHandle, + GLuint numBufferBarriers, + const BufferID *buffers, + GLuint numTextureBarriers, + const TextureID *textures, + const GLenum *srcLayouts) +{ + Semaphore *semaphore = getSemaphore(semaphoreHandle); + ASSERT(semaphore); + + BufferBarrierVector bufferBarriers(numBufferBarriers); + for (GLuint bufferBarrierIdx = 0; bufferBarrierIdx < numBufferBarriers; bufferBarrierIdx++) + { + bufferBarriers[bufferBarrierIdx] = getBuffer(buffers[bufferBarrierIdx]); + } + + TextureBarrierVector textureBarriers(numTextureBarriers); + for (GLuint textureBarrierIdx = 0; textureBarrierIdx < numTextureBarriers; textureBarrierIdx++) + { + textureBarriers[textureBarrierIdx].texture = getTexture(textures[textureBarrierIdx]); + textureBarriers[textureBarrierIdx].layout = srcLayouts[textureBarrierIdx]; + } + + ANGLE_CONTEXT_TRY(semaphore->wait(this, bufferBarriers, textureBarriers)); +} + +void Context::signalSemaphore(SemaphoreID semaphoreHandle, + GLuint numBufferBarriers, + const BufferID *buffers, + GLuint numTextureBarriers, + const TextureID *textures, + const GLenum *dstLayouts) +{ + Semaphore *semaphore = getSemaphore(semaphoreHandle); + ASSERT(semaphore); + + BufferBarrierVector bufferBarriers(numBufferBarriers); + for (GLuint bufferBarrierIdx = 0; bufferBarrierIdx < numBufferBarriers; bufferBarrierIdx++) + { + bufferBarriers[bufferBarrierIdx] = getBuffer(buffers[bufferBarrierIdx]); + } + + TextureBarrierVector textureBarriers(numTextureBarriers); + for (GLuint textureBarrierIdx = 0; textureBarrierIdx < numTextureBarriers; textureBarrierIdx++) + { + textureBarriers[textureBarrierIdx].texture = getTexture(textures[textureBarrierIdx]); + textureBarriers[textureBarrierIdx].layout = dstLayouts[textureBarrierIdx]; + } + + ANGLE_CONTEXT_TRY(semaphore->signal(this, bufferBarriers, textureBarriers)); +} + +void Context::importSemaphoreFd(SemaphoreID semaphore, HandleType handleType, GLint fd) +{ + Semaphore *semaphoreObject = getSemaphore(semaphore); + ASSERT(semaphoreObject != nullptr); + ANGLE_CONTEXT_TRY(semaphoreObject->importFd(this, handleType, fd)); +} + +void Context::importSemaphoreZirconHandle(SemaphoreID semaphore, + HandleType handleType, + GLuint handle) +{ + Semaphore *semaphoreObject = getSemaphore(semaphore); + ASSERT(semaphoreObject != nullptr); + ANGLE_CONTEXT_TRY(semaphoreObject->importZirconHandle(this, handleType, handle)); +} + +void Context::framebufferMemorylessPixelLocalStorage(GLint plane, GLenum internalformat) +{ + Framebuffer *framebuffer = mState.getDrawFramebuffer(); + ASSERT(framebuffer); + PixelLocalStorage &pls = framebuffer->getPixelLocalStorage(this); + + if (internalformat == GL_NONE) + { + pls.deinitialize(this, plane); + } + else + { + pls.setMemoryless(this, plane, internalformat); + } +} + +void Context::framebufferTexturePixelLocalStorage(GLint plane, + TextureID backingtexture, + GLint level, + GLint layer) +{ + Framebuffer *framebuffer = mState.getDrawFramebuffer(); + ASSERT(framebuffer); + PixelLocalStorage &pls = framebuffer->getPixelLocalStorage(this); + + if (backingtexture.value == 0) + { + pls.deinitialize(this, plane); + } + else + { + Texture *tex = getTexture(backingtexture); + ASSERT(tex); // Validation guarantees this. + pls.setTextureBacked(this, plane, tex, level, layer); + } +} + +void Context::beginPixelLocalStorage(GLsizei planes, const GLenum loadops[], const void *cleardata) +{ + Framebuffer *framebuffer = mState.getDrawFramebuffer(); + ASSERT(framebuffer); + PixelLocalStorage &pls = framebuffer->getPixelLocalStorage(this); + + pls.begin(this, planes, loadops, cleardata); + mState.setPixelLocalStorageActive(true); +} + +void Context::endPixelLocalStorage() +{ + Framebuffer *framebuffer = mState.getDrawFramebuffer(); + ASSERT(framebuffer); + PixelLocalStorage &pls = framebuffer->getPixelLocalStorage(this); + + pls.end(this); + mState.setPixelLocalStorageActive(false); +} + +void Context::pixelLocalStorageBarrier() +{ + if (getExtensions().shaderPixelLocalStorageCoherentANGLE) + { + return; + } + + Framebuffer *framebuffer = mState.getDrawFramebuffer(); + ASSERT(framebuffer); + PixelLocalStorage &pls = framebuffer->getPixelLocalStorage(this); + + pls.barrier(this); +} + +void Context::eGLImageTargetTexStorage(GLenum target, GLeglImageOES image, const GLint *attrib_list) +{ + Texture *texture = getTextureByType(FromGLenum<TextureType>(target)); + egl::Image *imageObject = static_cast<egl::Image *>(image); + ANGLE_CONTEXT_TRY(texture->setStorageEGLImageTarget(this, FromGLenum<TextureType>(target), + imageObject, attrib_list)); +} + +void Context::eGLImageTargetTextureStorage(GLuint texture, + GLeglImageOES image, + const GLint *attrib_list) +{ + return; +} + +void Context::eGLImageTargetTexture2D(TextureType target, GLeglImageOES image) +{ + Texture *texture = getTextureByType(target); + egl::Image *imageObject = static_cast<egl::Image *>(image); + ANGLE_CONTEXT_TRY(texture->setEGLImageTarget(this, target, imageObject)); +} + +void Context::eGLImageTargetRenderbufferStorage(GLenum target, GLeglImageOES image) +{ + Renderbuffer *renderbuffer = mState.getCurrentRenderbuffer(); + egl::Image *imageObject = static_cast<egl::Image *>(image); + ANGLE_CONTEXT_TRY(renderbuffer->setStorageEGLImageTarget(this, imageObject)); +} + +void Context::framebufferFetchBarrier() +{ + mImplementation->framebufferFetchBarrier(); +} + +void Context::texStorage1D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) +{ + UNIMPLEMENTED(); +} + +bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *numParams) const +{ + return GetQueryParameterInfo(mState, pname, type, numParams); +} + +bool Context::getIndexedQueryParameterInfo(GLenum target, + GLenum *type, + unsigned int *numParams) const +{ + if (getClientVersion() < Version(3, 0)) + { + return false; + } + + switch (target) + { + case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: + case GL_UNIFORM_BUFFER_BINDING: + { + *type = GL_INT; + *numParams = 1; + return true; + } + case GL_TRANSFORM_FEEDBACK_BUFFER_START: + case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: + case GL_UNIFORM_BUFFER_START: + case GL_UNIFORM_BUFFER_SIZE: + { + *type = GL_INT_64_ANGLEX; + *numParams = 1; + return true; + } + } + + if (mSupportedExtensions.drawBuffersIndexedAny()) + { + switch (target) + { + case GL_BLEND_SRC_RGB: + case GL_BLEND_SRC_ALPHA: + case GL_BLEND_DST_RGB: + case GL_BLEND_DST_ALPHA: + case GL_BLEND_EQUATION_RGB: + case GL_BLEND_EQUATION_ALPHA: + { + *type = GL_INT; + *numParams = 1; + return true; + } + case GL_COLOR_WRITEMASK: + { + *type = GL_BOOL; + *numParams = 4; + return true; + } + } + } + + if (mSupportedExtensions.shaderPixelLocalStorageANGLE) + { + switch (target) + { + case GL_PIXEL_LOCAL_FORMAT_ANGLE: + case GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE: + case GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE: + case GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE: + *type = GL_INT; + *numParams = 1; + return true; + } + } + + if (getClientVersion() < Version(3, 1)) + { + return false; + } + + switch (target) + { + case GL_IMAGE_BINDING_LAYERED: + { + *type = GL_BOOL; + *numParams = 1; + return true; + } + case GL_MAX_COMPUTE_WORK_GROUP_COUNT: + case GL_MAX_COMPUTE_WORK_GROUP_SIZE: + case GL_ATOMIC_COUNTER_BUFFER_BINDING: + case GL_SHADER_STORAGE_BUFFER_BINDING: + case GL_VERTEX_BINDING_BUFFER: + case GL_VERTEX_BINDING_DIVISOR: + case GL_VERTEX_BINDING_OFFSET: + case GL_VERTEX_BINDING_STRIDE: + case GL_SAMPLE_MASK_VALUE: + case GL_IMAGE_BINDING_NAME: + case GL_IMAGE_BINDING_LEVEL: + case GL_IMAGE_BINDING_LAYER: + case GL_IMAGE_BINDING_ACCESS: + case GL_IMAGE_BINDING_FORMAT: + { + *type = GL_INT; + *numParams = 1; + return true; + } + case GL_ATOMIC_COUNTER_BUFFER_START: + case GL_ATOMIC_COUNTER_BUFFER_SIZE: + case GL_SHADER_STORAGE_BUFFER_START: + case GL_SHADER_STORAGE_BUFFER_SIZE: + { + *type = GL_INT_64_ANGLEX; + *numParams = 1; + return true; + } + } + + return false; +} + +Program *Context::getProgramNoResolveLink(ShaderProgramID handle) const +{ + return mState.mShaderProgramManager->getProgram(handle); +} + +Shader *Context::getShader(ShaderProgramID handle) const +{ + return mState.mShaderProgramManager->getShader(handle); +} + +const angle::FrontendFeatures &Context::getFrontendFeatures() const +{ + return mDisplay->getFrontendFeatures(); +} + +bool Context::isRenderbufferGenerated(RenderbufferID renderbuffer) const +{ + return mState.mRenderbufferManager->isHandleGenerated(renderbuffer); +} + +bool Context::isFramebufferGenerated(FramebufferID framebuffer) const +{ + return mState.mFramebufferManager->isHandleGenerated(framebuffer); +} + +bool Context::isProgramPipelineGenerated(ProgramPipelineID pipeline) const +{ + return mState.mProgramPipelineManager->isHandleGenerated(pipeline); +} + +bool Context::usingDisplayTextureShareGroup() const +{ + return mDisplayTextureShareGroup; +} + +bool Context::usingDisplaySemaphoreShareGroup() const +{ + return mDisplaySemaphoreShareGroup; +} + +GLenum Context::getConvertedRenderbufferFormat(GLenum internalformat) const +{ + if (isWebGL() && mState.mClientVersion.major == 2 && internalformat == GL_DEPTH_STENCIL) + { + return GL_DEPTH24_STENCIL8; + } + if (getClientType() == EGL_OPENGL_API && internalformat == GL_DEPTH_COMPONENT) + { + return GL_DEPTH_COMPONENT24; + } + return internalformat; +} + +void Context::maxShaderCompilerThreads(GLuint count) +{ + GLuint oldCount = mState.getMaxShaderCompilerThreads(); + mState.setMaxShaderCompilerThreads(count); + // A count of zero specifies a request for no parallel compiling or linking. + if ((oldCount == 0 || count == 0) && (oldCount != 0 || count != 0)) + { + mMultiThreadPool = angle::WorkerThreadPool::Create(count > 0); + } + mMultiThreadPool->setMaxThreads(count); + mImplementation->setMaxShaderCompilerThreads(count); +} + +void Context::framebufferParameteriMESA(GLenum target, GLenum pname, GLint param) +{ + framebufferParameteri(target, pname, param); +} + +void Context::getFramebufferParameterivMESA(GLenum target, GLenum pname, GLint *params) +{ + getFramebufferParameteriv(target, pname, params); +} + +bool Context::isGLES1() const +{ + return mState.isGLES1(); +} + +void Context::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) +{ + switch (index) + { + case kVertexArraySubjectIndex: + switch (message) + { + case angle::SubjectMessage::ContentsChanged: + mState.setObjectDirty(GL_VERTEX_ARRAY); + mStateCache.onVertexArrayBufferContentsChange(this); + break; + case angle::SubjectMessage::SubjectMapped: + case angle::SubjectMessage::SubjectUnmapped: + case angle::SubjectMessage::BindingChanged: + mStateCache.onVertexArrayBufferStateChange(this); + break; + default: + break; + } + break; + + case kReadFramebufferSubjectIndex: + switch (message) + { + case angle::SubjectMessage::DirtyBitsFlagged: + mState.setReadFramebufferDirty(); + break; + case angle::SubjectMessage::SurfaceChanged: + mState.setReadFramebufferBindingDirty(); + break; + default: + UNREACHABLE(); + break; + } + break; + + case kDrawFramebufferSubjectIndex: + switch (message) + { + case angle::SubjectMessage::DirtyBitsFlagged: + mState.setDrawFramebufferDirty(); + mStateCache.onDrawFramebufferChange(this); + break; + case angle::SubjectMessage::SurfaceChanged: + mState.setDrawFramebufferBindingDirty(); + break; + default: + UNREACHABLE(); + break; + } + break; + + case kProgramPipelineSubjectIndex: + switch (message) + { + case angle::SubjectMessage::SubjectChanged: + ANGLE_CONTEXT_TRY(mState.onProgramPipelineExecutableChange(this)); + mStateCache.onProgramExecutableChange(this); + break; + case angle::SubjectMessage::ProgramRelinked: + ANGLE_CONTEXT_TRY(mState.mProgramPipeline->link(this)); + break; + default: + UNREACHABLE(); + break; + } + break; + + default: + if (index < kTextureMaxSubjectIndex) + { + if (message != angle::SubjectMessage::ContentsChanged && + message != angle::SubjectMessage::BindingChanged) + { + mState.onActiveTextureStateChange(this, index); + mStateCache.onActiveTextureChange(this); + } + } + else if (index < kImageMaxSubjectIndex) + { + mState.onImageStateChange(this, index - kImage0SubjectIndex); + if (message == angle::SubjectMessage::ContentsChanged) + { + mState.mDirtyBits.set(State::DirtyBitType::DIRTY_BIT_IMAGE_BINDINGS); + } + } + else if (index < kUniformBufferMaxSubjectIndex) + { + mState.onUniformBufferStateChange(index - kUniformBuffer0SubjectIndex); + mStateCache.onUniformBufferStateChange(this); + } + else if (index < kAtomicCounterBufferMaxSubjectIndex) + { + mState.onAtomicCounterBufferStateChange(index - kAtomicCounterBuffer0SubjectIndex); + mStateCache.onAtomicCounterBufferStateChange(this); + } + else if (index < kShaderStorageBufferMaxSubjectIndex) + { + mState.onShaderStorageBufferStateChange(index - kShaderStorageBuffer0SubjectIndex); + mStateCache.onShaderStorageBufferStateChange(this); + } + else + { + ASSERT(index < kSamplerMaxSubjectIndex); + mState.setSamplerDirty(index - kSampler0SubjectIndex); + mState.onActiveTextureStateChange(this, index - kSampler0SubjectIndex); + } + break; + } +} + +angle::Result Context::onProgramLink(Program *programObject) +{ + // Don't parallel link a program which is active in any GL contexts. With this assumption, we + // don't need to worry that: + // 1. Draw calls after link use the new executable code or the old one depending on the link + // result. + // 2. When a backend program, e.g., ProgramD3D is linking, other backend classes like + // StateManager11, Renderer11, etc., may have a chance to make unexpected calls to + // ProgramD3D. + if (programObject->isInUse()) + { + programObject->resolveLink(this); + if (programObject->isLinked()) + { + ANGLE_TRY(mState.onProgramExecutableChange(this, programObject)); + programObject->onStateChange(angle::SubjectMessage::ProgramRelinked); + } + mStateCache.onProgramExecutableChange(this); + } + + return angle::Result::Continue; +} + +egl::Error Context::setDefaultFramebuffer(egl::Surface *drawSurface, egl::Surface *readSurface) +{ + ASSERT(mCurrentDrawSurface == nullptr); + ASSERT(mCurrentReadSurface == nullptr); + + mCurrentDrawSurface = drawSurface; + mCurrentReadSurface = readSurface; + + if (drawSurface != nullptr) + { + ANGLE_TRY(drawSurface->makeCurrent(this)); + } + + ANGLE_TRY(mDefaultFramebuffer->setSurfaces(this, drawSurface, readSurface)); + + if (readSurface && (drawSurface != readSurface)) + { + ANGLE_TRY(readSurface->makeCurrent(this)); + } + + // Update default framebuffer, the binding of the previous default + // framebuffer (or lack of) will have a nullptr. + mState.mFramebufferManager->setDefaultFramebuffer(mDefaultFramebuffer.get()); + if (mState.getDrawFramebuffer() == nullptr) + { + bindDrawFramebuffer(mDefaultFramebuffer->id()); + } + if (mState.getReadFramebuffer() == nullptr) + { + bindReadFramebuffer(mDefaultFramebuffer->id()); + } + + return egl::NoError(); +} + +egl::Error Context::unsetDefaultFramebuffer() +{ + Framebuffer *defaultFramebuffer = + mState.mFramebufferManager->getFramebuffer(Framebuffer::kDefaultDrawFramebufferHandle); + + if (defaultFramebuffer) + { + // Remove the default framebuffer + if (defaultFramebuffer == mState.getReadFramebuffer()) + { + mState.setReadFramebufferBinding(nullptr); + mReadFramebufferObserverBinding.bind(nullptr); + } + + if (defaultFramebuffer == mState.getDrawFramebuffer()) + { + mState.setDrawFramebufferBinding(nullptr); + mDrawFramebufferObserverBinding.bind(nullptr); + } + + ANGLE_TRY(defaultFramebuffer->unsetSurfaces(this)); + mState.mFramebufferManager->setDefaultFramebuffer(nullptr); + } + + // Always unset the current surface, even if setIsCurrent fails. + egl::Surface *drawSurface = mCurrentDrawSurface; + egl::Surface *readSurface = mCurrentReadSurface; + mCurrentDrawSurface = nullptr; + mCurrentReadSurface = nullptr; + if (drawSurface) + { + ANGLE_TRY(drawSurface->unMakeCurrent(this)); + } + if (drawSurface != readSurface) + { + ANGLE_TRY(readSurface->unMakeCurrent(this)); + } + + return egl::NoError(); +} + +void Context::onPreSwap() const +{ + // Dump frame capture if enabled. + getShareGroup()->getFrameCaptureShared()->onEndFrame(this); +} + +void Context::getTexImage(TextureTarget target, + GLint level, + GLenum format, + GLenum type, + void *pixels) +{ + Texture *texture = getTextureByTarget(target); + Buffer *packBuffer = mState.getTargetBuffer(BufferBinding::PixelPack); + ANGLE_CONTEXT_TRY(texture->getTexImage(this, mState.getPackState(), packBuffer, target, level, + format, type, pixels)); +} + +void Context::getCompressedTexImage(TextureTarget target, GLint level, void *pixels) +{ + Texture *texture = getTextureByTarget(target); + Buffer *packBuffer = mState.getTargetBuffer(BufferBinding::PixelPack); + ANGLE_CONTEXT_TRY(texture->getCompressedTexImage(this, mState.getPackState(), packBuffer, + target, level, pixels)); +} + +void Context::getRenderbufferImage(GLenum target, GLenum format, GLenum type, void *pixels) +{ + Renderbuffer *renderbuffer = mState.getCurrentRenderbuffer(); + Buffer *packBuffer = mState.getTargetBuffer(BufferBinding::PixelPack); + ANGLE_CONTEXT_TRY(renderbuffer->getRenderbufferImage(this, mState.getPackState(), packBuffer, + format, type, pixels)); +} + +void Context::logicOpANGLE(LogicalOperation opcodePacked) +{ + mState.setLogicOp(opcodePacked); +} + +egl::Error Context::releaseHighPowerGPU() +{ + return mImplementation->releaseHighPowerGPU(this); +} + +egl::Error Context::reacquireHighPowerGPU() +{ + return mImplementation->reacquireHighPowerGPU(this); +} + +void Context::onGPUSwitch() +{ + // Re-initialize the renderer string, which just changed, and + // which must be visible to applications. + initRendererString(); +} + +std::mutex &Context::getProgramCacheMutex() const +{ + return mDisplay->getProgramCacheMutex(); +} + +bool Context::supportsGeometryOrTesselation() const +{ + return mState.getClientVersion() == ES_3_2 || mState.getExtensions().geometryShaderAny() || + mState.getExtensions().tessellationShaderEXT; +} + +void Context::dirtyAllState() +{ + mState.setAllDirtyBits(); + mState.setAllDirtyObjects(); + mState.gles1().setAllDirty(); +} + +void Context::finishImmutable() const +{ + ANGLE_CONTEXT_TRY(mImplementation->finish(this)); +} + +void Context::beginPerfMonitor(GLuint monitor) {} + +void Context::deletePerfMonitors(GLsizei n, GLuint *monitors) {} + +void Context::endPerfMonitor(GLuint monitor) {} + +void Context::genPerfMonitors(GLsizei n, GLuint *monitors) +{ + for (GLsizei monitorIndex = 0; monitorIndex < n; ++monitorIndex) + { + monitors[n] = static_cast<GLuint>(monitorIndex); + } +} + +void Context::getPerfMonitorCounterData(GLuint monitor, + GLenum pname, + GLsizei dataSize, + GLuint *data, + GLint *bytesWritten) +{ + using namespace angle; + const PerfMonitorCounterGroups &perfMonitorGroups = mImplementation->getPerfMonitorCounters(); + GLint byteCount = 0; + switch (pname) + { + case GL_PERFMON_RESULT_AVAILABLE_AMD: + { + *data = GL_TRUE; + byteCount += sizeof(GLuint); + break; + } + case GL_PERFMON_RESULT_SIZE_AMD: + { + GLuint resultSize = 0; + for (const PerfMonitorCounterGroup &group : perfMonitorGroups) + { + resultSize += sizeof(PerfMonitorTriplet) * group.counters.size(); + } + *data = resultSize; + byteCount += sizeof(GLuint); + break; + } + case GL_PERFMON_RESULT_AMD: + { + PerfMonitorTriplet *resultsOut = reinterpret_cast<PerfMonitorTriplet *>(data); + GLsizei maxResults = dataSize / (3 * sizeof(GLuint)); + GLsizei resultCount = 0; + for (size_t groupIndex = 0; + groupIndex < perfMonitorGroups.size() && resultCount < maxResults; ++groupIndex) + { + const PerfMonitorCounterGroup &group = perfMonitorGroups[groupIndex]; + for (size_t counterIndex = 0; + counterIndex < group.counters.size() && resultCount < maxResults; + ++counterIndex) + { + const PerfMonitorCounter &counter = group.counters[counterIndex]; + PerfMonitorTriplet &triplet = resultsOut[resultCount++]; + triplet.counter = static_cast<GLuint>(counterIndex); + triplet.group = static_cast<GLuint>(groupIndex); + triplet.value = counter.value; + } + } + byteCount += sizeof(PerfMonitorTriplet) * resultCount; + break; + } + default: + UNREACHABLE(); + } + + if (bytesWritten) + { + *bytesWritten = byteCount; + } +} + +void Context::getPerfMonitorCounterInfo(GLuint group, GLuint counter, GLenum pname, void *data) +{ + using namespace angle; + const PerfMonitorCounterGroups &perfMonitorGroups = mImplementation->getPerfMonitorCounters(); + ASSERT(group < perfMonitorGroups.size()); + const PerfMonitorCounters &counters = perfMonitorGroups[group].counters; + ASSERT(counter < counters.size()); + + switch (pname) + { + case GL_COUNTER_TYPE_AMD: + { + GLenum *dataOut = reinterpret_cast<GLenum *>(data); + *dataOut = GL_UNSIGNED_INT; + break; + } + case GL_COUNTER_RANGE_AMD: + { + GLuint *dataOut = reinterpret_cast<GLuint *>(data); + dataOut[0] = 0; + dataOut[1] = std::numeric_limits<GLuint>::max(); + break; + } + default: + UNREACHABLE(); + } +} + +void Context::getPerfMonitorCounterString(GLuint group, + GLuint counter, + GLsizei bufSize, + GLsizei *length, + GLchar *counterString) +{ + using namespace angle; + const PerfMonitorCounterGroups &perfMonitorGroups = mImplementation->getPerfMonitorCounters(); + ASSERT(group < perfMonitorGroups.size()); + const PerfMonitorCounters &counters = perfMonitorGroups[group].counters; + ASSERT(counter < counters.size()); + GetPerfMonitorString(counters[counter].name, bufSize, length, counterString); +} + +void Context::getPerfMonitorCounters(GLuint group, + GLint *numCounters, + GLint *maxActiveCounters, + GLsizei counterSize, + GLuint *counters) +{ + using namespace angle; + const PerfMonitorCounterGroups &perfMonitorGroups = mImplementation->getPerfMonitorCounters(); + ASSERT(group < perfMonitorGroups.size()); + const PerfMonitorCounters &groupCounters = perfMonitorGroups[group].counters; + + if (numCounters) + { + *numCounters = static_cast<GLint>(groupCounters.size()); + } + + if (maxActiveCounters) + { + *maxActiveCounters = static_cast<GLint>(groupCounters.size()); + } + + if (counters) + { + GLsizei maxCounterIndex = std::min(counterSize, static_cast<GLsizei>(groupCounters.size())); + for (GLsizei counterIndex = 0; counterIndex < maxCounterIndex; ++counterIndex) + { + counters[counterIndex] = static_cast<GLuint>(counterIndex); + } + } +} + +void Context::getPerfMonitorGroupString(GLuint group, + GLsizei bufSize, + GLsizei *length, + GLchar *groupString) +{ + using namespace angle; + const PerfMonitorCounterGroups &perfMonitorGroups = mImplementation->getPerfMonitorCounters(); + ASSERT(group < perfMonitorGroups.size()); + GetPerfMonitorString(perfMonitorGroups[group].name, bufSize, length, groupString); +} + +void Context::getPerfMonitorGroups(GLint *numGroups, GLsizei groupsSize, GLuint *groups) +{ + using namespace angle; + const PerfMonitorCounterGroups &perfMonitorGroups = mImplementation->getPerfMonitorCounters(); + + if (numGroups) + { + *numGroups = static_cast<GLint>(perfMonitorGroups.size()); + } + + GLuint maxGroupIndex = + std::min<GLuint>(groupsSize, static_cast<GLuint>(perfMonitorGroups.size())); + for (GLuint groupIndex = 0; groupIndex < maxGroupIndex; ++groupIndex) + { + groups[groupIndex] = groupIndex; + } +} + +void Context::selectPerfMonitorCounters(GLuint monitor, + GLboolean enable, + GLuint group, + GLint numCounters, + GLuint *counterList) +{} + +const angle::PerfMonitorCounterGroups &Context::getPerfMonitorCounterGroups() const +{ + return mImplementation->getPerfMonitorCounters(); +} + +// ErrorSet implementation. +ErrorSet::ErrorSet(Context *context) : mContext(context) {} + +ErrorSet::~ErrorSet() = default; + +void ErrorSet::handleError(GLenum errorCode, + const char *message, + const char *file, + const char *function, + unsigned int line) +{ + if (errorCode == GL_OUT_OF_MEMORY && + mContext->getGraphicsResetStrategy() == GL_LOSE_CONTEXT_ON_RESET_EXT && + mContext->getDisplay()->getFrontendFeatures().loseContextOnOutOfMemory.enabled) + { + mContext->markContextLost(GraphicsResetStatus::UnknownContextReset); + } + + std::stringstream errorStream; + errorStream << "Error: " << gl::FmtHex(errorCode) << ", in " << file << ", " << function << ":" + << line << ". " << message; + + std::string formattedMessage = errorStream.str(); + + // Process the error, but log it with WARN severity so it shows up in logs. + ASSERT(errorCode != GL_NO_ERROR); + mErrors.insert(errorCode); + + mContext->getState().getDebug().insertMessage( + GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, errorCode, GL_DEBUG_SEVERITY_HIGH, + std::move(formattedMessage), gl::LOG_WARN, angle::EntryPoint::GLInvalid); +} + +void ErrorSet::validationError(angle::EntryPoint entryPoint, GLenum errorCode, const char *message) +{ + ASSERT(errorCode != GL_NO_ERROR); + mErrors.insert(errorCode); + + mContext->getState().getDebug().insertMessage(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, + errorCode, GL_DEBUG_SEVERITY_HIGH, message, + gl::LOG_INFO, entryPoint); +} + +bool ErrorSet::empty() const +{ + return mErrors.empty(); +} + +GLenum ErrorSet::popError() +{ + ASSERT(!empty()); + GLenum error = *mErrors.begin(); + mErrors.erase(mErrors.begin()); + return error; +} + +// StateCache implementation. +StateCache::StateCache() + : mCachedHasAnyEnabledClientAttrib(false), + mCachedNonInstancedVertexElementLimit(0), + mCachedInstancedVertexElementLimit(0), + mCachedBasicDrawStatesError(kInvalidPointer), + mCachedBasicDrawElementsError(kInvalidPointer), + mCachedProgramPipelineError(kInvalidPointer), + mCachedTransformFeedbackActiveUnpaused(false), + mCachedCanDraw(false) +{ + mCachedValidDrawModes.fill(false); +} + +StateCache::~StateCache() = default; + +ANGLE_INLINE void StateCache::updateVertexElementLimits(Context *context) +{ + if (context->isBufferAccessValidationEnabled()) + { + updateVertexElementLimitsImpl(context); + } +} + +void StateCache::initialize(Context *context) +{ + updateValidDrawModes(context); + updateValidBindTextureTypes(context); + updateValidDrawElementsTypes(context); + updateBasicDrawStatesError(); + updateBasicDrawElementsError(); + updateVertexAttribTypesValidation(context); + updateCanDraw(context); +} + +void StateCache::updateActiveAttribsMask(Context *context) +{ + bool isGLES1 = context->isGLES1(); + const State &glState = context->getState(); + + if (!isGLES1 && !glState.getProgramExecutable()) + { + mCachedActiveBufferedAttribsMask = AttributesMask(); + mCachedActiveClientAttribsMask = AttributesMask(); + mCachedActiveDefaultAttribsMask = AttributesMask(); + return; + } + + AttributesMask activeAttribs = + isGLES1 ? glState.gles1().getActiveAttributesMask() + : glState.getProgramExecutable()->getActiveAttribLocationsMask(); + + const VertexArray *vao = glState.getVertexArray(); + ASSERT(vao); + + const AttributesMask &clientAttribs = vao->getClientAttribsMask(); + const AttributesMask &enabledAttribs = vao->getEnabledAttributesMask(); + const AttributesMask &activeEnabled = activeAttribs & enabledAttribs; + + mCachedActiveClientAttribsMask = activeEnabled & clientAttribs; + mCachedActiveBufferedAttribsMask = activeEnabled & ~clientAttribs; + mCachedActiveDefaultAttribsMask = activeAttribs & ~enabledAttribs; + mCachedHasAnyEnabledClientAttrib = (clientAttribs & enabledAttribs).any(); +} + +void StateCache::updateVertexElementLimitsImpl(Context *context) +{ + ASSERT(context->isBufferAccessValidationEnabled()); + + const VertexArray *vao = context->getState().getVertexArray(); + + mCachedNonInstancedVertexElementLimit = std::numeric_limits<GLint64>::max(); + mCachedInstancedVertexElementLimit = std::numeric_limits<GLint64>::max(); + + // VAO can be null on Context startup. If we make this computation lazier we could ASSERT. + // If there are no buffered attributes then we should not limit the draw call count. + if (!vao || !mCachedActiveBufferedAttribsMask.any()) + { + return; + } + + const auto &vertexAttribs = vao->getVertexAttributes(); + const auto &vertexBindings = vao->getVertexBindings(); + + for (size_t attributeIndex : mCachedActiveBufferedAttribsMask) + { + const VertexAttribute &attrib = vertexAttribs[attributeIndex]; + + const VertexBinding &binding = vertexBindings[attrib.bindingIndex]; + ASSERT(context->isGLES1() || + context->getState().getProgramExecutable()->isAttribLocationActive(attributeIndex)); + + GLint64 limit = attrib.getCachedElementLimit(); + if (binding.getDivisor() > 0) + { + mCachedInstancedVertexElementLimit = + std::min(mCachedInstancedVertexElementLimit, limit); + } + else + { + mCachedNonInstancedVertexElementLimit = + std::min(mCachedNonInstancedVertexElementLimit, limit); + } + } +} + +void StateCache::updateBasicDrawStatesError() +{ + mCachedBasicDrawStatesError = kInvalidPointer; +} + +void StateCache::updateProgramPipelineError() +{ + mCachedProgramPipelineError = kInvalidPointer; +} + +void StateCache::updateBasicDrawElementsError() +{ + mCachedBasicDrawElementsError = kInvalidPointer; +} + +intptr_t StateCache::getBasicDrawStatesErrorImpl(const Context *context) const +{ + ASSERT(mCachedBasicDrawStatesError == kInvalidPointer); + mCachedBasicDrawStatesError = reinterpret_cast<intptr_t>(ValidateDrawStates(context)); + return mCachedBasicDrawStatesError; +} + +intptr_t StateCache::getProgramPipelineErrorImpl(const Context *context) const +{ + ASSERT(mCachedProgramPipelineError == kInvalidPointer); + mCachedProgramPipelineError = reinterpret_cast<intptr_t>(ValidateProgramPipeline(context)); + return mCachedProgramPipelineError; +} + +intptr_t StateCache::getBasicDrawElementsErrorImpl(const Context *context) const +{ + ASSERT(mCachedBasicDrawElementsError == kInvalidPointer); + mCachedBasicDrawElementsError = reinterpret_cast<intptr_t>(ValidateDrawElementsStates(context)); + return mCachedBasicDrawElementsError; +} + +void StateCache::onVertexArrayBindingChange(Context *context) +{ + updateActiveAttribsMask(context); + updateVertexElementLimits(context); + updateBasicDrawStatesError(); + updateBasicDrawElementsError(); +} + +void StateCache::onProgramExecutableChange(Context *context) +{ + updateActiveAttribsMask(context); + updateVertexElementLimits(context); + updateBasicDrawStatesError(); + updateProgramPipelineError(); + updateValidDrawModes(context); + updateActiveShaderStorageBufferIndices(context); + updateActiveImageUnitIndices(context); + updateCanDraw(context); +} + +void StateCache::onVertexArrayFormatChange(Context *context) +{ + updateVertexElementLimits(context); +} + +void StateCache::onVertexArrayBufferContentsChange(Context *context) +{ + updateVertexElementLimits(context); + updateBasicDrawStatesError(); +} + +void StateCache::onVertexArrayStateChange(Context *context) +{ + updateActiveAttribsMask(context); + updateVertexElementLimits(context); + updateBasicDrawStatesError(); + updateBasicDrawElementsError(); +} + +void StateCache::onVertexArrayBufferStateChange(Context *context) +{ + updateBasicDrawStatesError(); + updateBasicDrawElementsError(); +} + +void StateCache::onGLES1ClientStateChange(Context *context) +{ + updateActiveAttribsMask(context); +} + +void StateCache::onDrawFramebufferChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onContextCapChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onStencilStateChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onDefaultVertexAttributeChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onActiveTextureChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onQueryChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onActiveTransformFeedbackChange(Context *context) +{ + updateTransformFeedbackActiveUnpaused(context); + updateBasicDrawStatesError(); + updateBasicDrawElementsError(); + updateValidDrawModes(context); +} + +void StateCache::onUniformBufferStateChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onAtomicCounterBufferStateChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onShaderStorageBufferStateChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onColorMaskChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onBlendFuncIndexedChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::onBlendEquationChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::setValidDrawModes(bool pointsOK, + bool linesOK, + bool trisOK, + bool lineAdjOK, + bool triAdjOK, + bool patchOK) +{ + mCachedValidDrawModes[PrimitiveMode::Points] = pointsOK; + mCachedValidDrawModes[PrimitiveMode::Lines] = linesOK; + mCachedValidDrawModes[PrimitiveMode::LineLoop] = linesOK; + mCachedValidDrawModes[PrimitiveMode::LineStrip] = linesOK; + mCachedValidDrawModes[PrimitiveMode::Triangles] = trisOK; + mCachedValidDrawModes[PrimitiveMode::TriangleStrip] = trisOK; + mCachedValidDrawModes[PrimitiveMode::TriangleFan] = trisOK; + mCachedValidDrawModes[PrimitiveMode::LinesAdjacency] = lineAdjOK; + mCachedValidDrawModes[PrimitiveMode::LineStripAdjacency] = lineAdjOK; + mCachedValidDrawModes[PrimitiveMode::TrianglesAdjacency] = triAdjOK; + mCachedValidDrawModes[PrimitiveMode::TriangleStripAdjacency] = triAdjOK; + mCachedValidDrawModes[PrimitiveMode::Patches] = patchOK; +} + +void StateCache::updateValidDrawModes(Context *context) +{ + const State &state = context->getState(); + + const ProgramExecutable *programExecutable = context->getState().getProgramExecutable(); + + // If tessellation is active primitive mode must be GL_PATCHES. + if (programExecutable && programExecutable->hasLinkedTessellationShader()) + { + setValidDrawModes(false, false, false, false, false, true); + return; + } + + if (mCachedTransformFeedbackActiveUnpaused) + { + TransformFeedback *curTransformFeedback = state.getCurrentTransformFeedback(); + + // ES Spec 3.0 validation text: + // When transform feedback is active and not paused, all geometric primitives generated must + // match the value of primitiveMode passed to BeginTransformFeedback. The error + // INVALID_OPERATION is generated by DrawArrays and DrawArraysInstanced if mode is not + // identical to primitiveMode. The error INVALID_OPERATION is also generated by + // DrawElements, DrawElementsInstanced, and DrawRangeElements while transform feedback is + // active and not paused, regardless of mode. Any primitive type may be used while transform + // feedback is paused. + if (!context->getExtensions().geometryShaderAny() && + !context->getExtensions().tessellationShaderEXT && context->getClientVersion() < ES_3_2) + { + mCachedValidDrawModes.fill(false); + mCachedValidDrawModes[curTransformFeedback->getPrimitiveMode()] = true; + return; + } + } + + if (!programExecutable || !programExecutable->hasLinkedShaderStage(ShaderType::Geometry)) + { + // All draw modes are valid, since drawing without a program does not generate an error and + // and operations requiring a GS will trigger other validation errors. + // `patchOK = false` due to checking above already enabling it if a TS is present. + setValidDrawModes(true, true, true, true, true, false); + return; + } + + PrimitiveMode gsMode = programExecutable->getGeometryShaderInputPrimitiveType(); + bool pointsOK = gsMode == PrimitiveMode::Points; + bool linesOK = gsMode == PrimitiveMode::Lines; + bool trisOK = gsMode == PrimitiveMode::Triangles; + bool lineAdjOK = gsMode == PrimitiveMode::LinesAdjacency; + bool triAdjOK = gsMode == PrimitiveMode::TrianglesAdjacency; + + setValidDrawModes(pointsOK, linesOK, trisOK, lineAdjOK, triAdjOK, false); +} + +void StateCache::updateValidBindTextureTypes(Context *context) +{ + const Extensions &exts = context->getExtensions(); + bool isGLES3 = context->getClientMajorVersion() >= 3; + bool isGLES31 = context->getClientVersion() >= Version(3, 1); + + mCachedValidBindTextureTypes = {{ + {TextureType::_2D, true}, + {TextureType::_2DArray, isGLES3}, + {TextureType::_2DMultisample, isGLES31 || exts.textureMultisampleANGLE}, + {TextureType::_2DMultisampleArray, exts.textureStorageMultisample2dArrayOES}, + {TextureType::_3D, isGLES3 || exts.texture3DOES}, + {TextureType::External, exts.EGLImageExternalOES || exts.EGLStreamConsumerExternalNV}, + {TextureType::Rectangle, exts.textureRectangleANGLE}, + {TextureType::CubeMap, true}, + {TextureType::CubeMapArray, exts.textureCubeMapArrayAny()}, + {TextureType::VideoImage, exts.videoTextureWEBGL}, + {TextureType::Buffer, exts.textureBufferAny()}, + }}; +} + +void StateCache::updateValidDrawElementsTypes(Context *context) +{ + bool supportsUint = + (context->getClientMajorVersion() >= 3 || context->getExtensions().elementIndexUintOES); + + mCachedValidDrawElementsTypes = {{ + {DrawElementsType::UnsignedByte, true}, + {DrawElementsType::UnsignedShort, true}, + {DrawElementsType::UnsignedInt, supportsUint}, + }}; +} + +void StateCache::updateTransformFeedbackActiveUnpaused(Context *context) +{ + TransformFeedback *xfb = context->getState().getCurrentTransformFeedback(); + mCachedTransformFeedbackActiveUnpaused = xfb && xfb->isActive() && !xfb->isPaused(); +} + +void StateCache::updateVertexAttribTypesValidation(Context *context) +{ + VertexAttribTypeCase halfFloatValidity = (context->getExtensions().vertexHalfFloatOES) + ? VertexAttribTypeCase::Valid + : VertexAttribTypeCase::Invalid; + + VertexAttribTypeCase vertexType1010102Validity = (context->getExtensions().vertexType1010102OES) + ? VertexAttribTypeCase::ValidSize3or4 + : VertexAttribTypeCase::Invalid; + + if (context->getClientMajorVersion() <= 2) + { + mCachedVertexAttribTypesValidation = {{ + {VertexAttribType::Byte, VertexAttribTypeCase::Valid}, + {VertexAttribType::Short, VertexAttribTypeCase::Valid}, + {VertexAttribType::UnsignedByte, VertexAttribTypeCase::Valid}, + {VertexAttribType::UnsignedShort, VertexAttribTypeCase::Valid}, + {VertexAttribType::Float, VertexAttribTypeCase::Valid}, + {VertexAttribType::Fixed, VertexAttribTypeCase::Valid}, + {VertexAttribType::HalfFloatOES, halfFloatValidity}, + }}; + } + else + { + mCachedVertexAttribTypesValidation = {{ + {VertexAttribType::Byte, VertexAttribTypeCase::Valid}, + {VertexAttribType::Short, VertexAttribTypeCase::Valid}, + {VertexAttribType::Int, VertexAttribTypeCase::Valid}, + {VertexAttribType::UnsignedByte, VertexAttribTypeCase::Valid}, + {VertexAttribType::UnsignedShort, VertexAttribTypeCase::Valid}, + {VertexAttribType::UnsignedInt, VertexAttribTypeCase::Valid}, + {VertexAttribType::Float, VertexAttribTypeCase::Valid}, + {VertexAttribType::HalfFloat, VertexAttribTypeCase::Valid}, + {VertexAttribType::Fixed, VertexAttribTypeCase::Valid}, + {VertexAttribType::Int2101010, VertexAttribTypeCase::ValidSize4Only}, + {VertexAttribType::HalfFloatOES, halfFloatValidity}, + {VertexAttribType::UnsignedInt2101010, VertexAttribTypeCase::ValidSize4Only}, + {VertexAttribType::Int1010102, vertexType1010102Validity}, + {VertexAttribType::UnsignedInt1010102, vertexType1010102Validity}, + }}; + + mCachedIntegerVertexAttribTypesValidation = {{ + {VertexAttribType::Byte, VertexAttribTypeCase::Valid}, + {VertexAttribType::Short, VertexAttribTypeCase::Valid}, + {VertexAttribType::Int, VertexAttribTypeCase::Valid}, + {VertexAttribType::UnsignedByte, VertexAttribTypeCase::Valid}, + {VertexAttribType::UnsignedShort, VertexAttribTypeCase::Valid}, + {VertexAttribType::UnsignedInt, VertexAttribTypeCase::Valid}, + }}; + } +} + +void StateCache::updateActiveShaderStorageBufferIndices(Context *context) +{ + mCachedActiveShaderStorageBufferIndices.reset(); + const ProgramExecutable *executable = context->getState().getProgramExecutable(); + if (executable) + { + for (const InterfaceBlock &block : executable->getShaderStorageBlocks()) + { + mCachedActiveShaderStorageBufferIndices.set(block.binding); + } + } +} + +void StateCache::updateActiveImageUnitIndices(Context *context) +{ + mCachedActiveImageUnitIndices.reset(); + const ProgramExecutable *executable = context->getState().getProgramExecutable(); + if (executable) + { + for (const ImageBinding &imageBinding : executable->getImageBindings()) + { + for (GLuint binding : imageBinding.boundImageUnits) + { + mCachedActiveImageUnitIndices.set(binding); + } + } + } +} + +void StateCache::updateCanDraw(Context *context) +{ + mCachedCanDraw = + (context->isGLES1() || (context->getState().getProgramExecutable() && + context->getState().getProgramExecutable()->hasVertexShader())); +} +} // namespace gl |