diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/angle/checkout/src/libANGLE/Context.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
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 | 8927 |
1 files changed, 8927 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..e834735074 --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/Context.cpp @@ -0,0 +1,8927 @@ +// +// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// 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.h" +#include "libANGLE/Context.inl.h" + +#include <string.h> +#include <iterator> +#include <sstream> +#include <vector> + +#include "common/PackedEnums.h" +#include "common/matrix_utils.h" +#include "common/platform.h" +#include "common/utilities.h" +#include "common/version.h" +#include "libANGLE/Buffer.h" +#include "libANGLE/Compiler.h" +#include "libANGLE/Display.h" +#include "libANGLE/Fence.h" +#include "libANGLE/Framebuffer.h" +#include "libANGLE/FramebufferAttachment.h" +#include "libANGLE/MemoryObject.h" +#include "libANGLE/Path.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/formatutils.h" +#include "libANGLE/queryconversions.h" +#include "libANGLE/queryutils.h" +#include "libANGLE/renderer/DisplayImpl.h" +#include "libANGLE/renderer/Format.h" +#include "libANGLE/validationES.h" + +namespace gl +{ +namespace +{ +template <typename T> +std::vector<Path *> GatherPaths(PathManager &resourceManager, + GLsizei numPaths, + const void *paths, + GLuint pathBase) +{ + std::vector<Path *> ret; + ret.reserve(numPaths); + + const auto *nameArray = static_cast<const T *>(paths); + + for (GLsizei i = 0; i < numPaths; ++i) + { + const GLuint pathName = nameArray[i] + pathBase; + + ret.push_back(resourceManager.getPath(pathName)); + } + + return ret; +} + +std::vector<Path *> GatherPaths(PathManager &resourceManager, + GLsizei numPaths, + GLenum pathNameType, + const void *paths, + GLuint pathBase) +{ + switch (pathNameType) + { + case GL_UNSIGNED_BYTE: + return GatherPaths<GLubyte>(resourceManager, numPaths, paths, pathBase); + + case GL_BYTE: + return GatherPaths<GLbyte>(resourceManager, numPaths, paths, pathBase); + + case GL_UNSIGNED_SHORT: + return GatherPaths<GLushort>(resourceManager, numPaths, paths, pathBase); + + case GL_SHORT: + return GatherPaths<GLshort>(resourceManager, numPaths, paths, pathBase); + + case GL_UNSIGNED_INT: + return GatherPaths<GLuint>(resourceManager, numPaths, paths, pathBase); + + case GL_INT: + return GatherPaths<GLint>(resourceManager, numPaths, paths, pathBase); + } + + UNREACHABLE(); + return std::vector<Path *>(); +} + +template <typename T> +angle::Result GetQueryObjectParameter(const Context *context, Query *query, GLenum pname, T *params) +{ + ASSERT(query != nullptr || pname == GL_QUERY_RESULT_AVAILABLE_EXT); + + 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; +} + +Version GetClientVersion(egl::Display *display, const egl::AttributeMap &attribs) +{ + Version requestedVersion = + Version(GetClientMajorVersion(attribs), GetClientMinorVersion(attribs)); + if (GetBackwardCompatibleContext(attribs)) + { + 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. + return std::max(display->getImplementation()->getMaxConformantESVersion(), + requestedVersion); + } + } + else + { + return requestedVersion; + } +} + +GLenum GetResetStrategy(const egl::AttributeMap &attribs) +{ + EGLAttrib attrib = + attribs.get(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, EGL_NO_RESET_NOTIFICATION); + switch (attrib) + { + 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) +{ + return (attribs.get(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_FALSE) == EGL_TRUE) || + ((attribs.get(EGL_CONTEXT_FLAGS_KHR, 0) & EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR) != + 0); +} + +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 GetWebGLContext(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE, 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(const egl::AttributeMap &attribs) +{ + return (attribs.get(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE, EGL_FALSE) == EGL_TRUE); +} + +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); + } +} + +template <typename CapT, typename MaxT> +void LimitCap(CapT *cap, MaxT maximum) +{ + *cap = std::min(*cap, static_cast<CapT>(maximum)); +} + +// The rest default to false. +constexpr angle::PackedEnumMap<PrimitiveMode, bool, angle::EnumSize<PrimitiveMode>() + 1> + kValidBasicDrawModes = {{ + {PrimitiveMode::Points, true}, + {PrimitiveMode::Lines, true}, + {PrimitiveMode::LineLoop, true}, + {PrimitiveMode::LineStrip, true}, + {PrimitiveMode::Triangles, true}, + {PrimitiveMode::TriangleStrip, true}, + {PrimitiveMode::TriangleFan, true}, + }}; + +enum SubjectIndexes : angle::SubjectIndex +{ + kTexture0SubjectIndex = 0, + kTextureMaxSubjectIndex = kTexture0SubjectIndex + IMPLEMENTATION_MAX_ACTIVE_TEXTURES, + kUniformBuffer0SubjectIndex = kTextureMaxSubjectIndex, + kUniformBufferMaxSubjectIndex = + kUniformBuffer0SubjectIndex + IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS, + kSampler0SubjectIndex = kUniformBufferMaxSubjectIndex, + kSamplerMaxSubjectIndex = kSampler0SubjectIndex + IMPLEMENTATION_MAX_ACTIVE_TEXTURES, + kVertexArraySubjectIndex = kSamplerMaxSubjectIndex, + kReadFramebufferSubjectIndex, + kDrawFramebufferSubjectIndex +}; +} // anonymous namespace + +Context::Context(egl::Display *display, + const egl::Config *config, + const Context *shareContext, + TextureManager *shareTextures, + MemoryProgramCache *memoryProgramCache, + const EGLenum clientType, + const egl::AttributeMap &attribs, + const egl::DisplayExtensions &displayExtensions, + const egl::ClientExtensions &clientExtensions) + : mState(reinterpret_cast<ContextID>(this), + shareContext ? &shareContext->mState : nullptr, + shareTextures, + clientType, + GetClientVersion(display, attribs), + GetDebug(attribs), + GetBindGeneratesResource(attribs), + GetClientArraysEnabled(attribs), + GetRobustResourceInit(attribs), + memoryProgramCache != nullptr), + mSkipValidation(GetNoError(attribs)), + mDisplayTextureShareGroup(shareTextures != 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)), + mRobustAccess(GetRobustAccess(attribs)), + mSurfacelessSupported(displayExtensions.surfacelessContext), + mExplicitContextAvailable(clientExtensions.explicitContext), + mCurrentSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)), + mDisplay(static_cast<egl::Display *>(EGL_NO_DISPLAY)), + mWebGLContext(GetWebGLContext(attribs)), + mBufferAccessValidationEnabled(false), + mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)), + mMemoryProgramCache(memoryProgramCache), + mVertexArrayObserverBinding(this, kVertexArraySubjectIndex), + mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex), + mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex), + mScratchBuffer(1000u), + mZeroFilledBuffer(1000u), + mThreadPool(nullptr) +{ + for (angle::SubjectIndex uboIndex = kUniformBuffer0SubjectIndex; + uboIndex < kUniformBufferMaxSubjectIndex; ++uboIndex) + { + mUniformBufferObserverBindings.emplace_back(this, uboIndex); + } + + for (angle::SubjectIndex samplerIndex = kSampler0SubjectIndex; + samplerIndex < kSamplerMaxSubjectIndex; ++samplerIndex) + { + mSamplerObserverBindings.emplace_back(this, samplerIndex); + } +} + +void Context::initialize() +{ + mImplementation->setMemoryProgramCache(mMemoryProgramCache); + + initCaps(); + + if (mDisplay->getFrontendFeatures().syncFramebufferBindingsOnTexImage.enabled) + { + mTexImageDirtyBits.set(State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); + mTexImageDirtyBits.set(State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); + } + + mState.initialize(this); + + 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.textureMultisample) + { + 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 (unsigned int i = 0; i < mState.mCaps.maxAtomicCounterBufferBindings; i++) + { + bindBufferRange(BufferBinding::AtomicCounter, i, 0, 0, 0); + } + + for (unsigned int i = 0; i < mState.mCaps.maxShaderStorageBufferBindings; i++) + { + bindBufferRange(BufferBinding::ShaderStorage, i, 0, 0, 0); + } + } + + if (mSupportedExtensions.textureRectangle) + { + Texture *zeroTextureRectangle = + new Texture(mImplementation.get(), 0, TextureType::Rectangle); + mZeroTextures[TextureType::Rectangle].set(this, zeroTextureRectangle); + } + + if (mSupportedExtensions.eglImageExternal || mSupportedExtensions.eglStreamConsumerExternal) + { + Texture *zeroTextureExternal = new Texture(mImplementation.get(), 0, TextureType::External); + mZeroTextures[TextureType::External].set(this, zeroTextureExternal); + } + + mState.initializeZeroTextures(this, mZeroTextures); + + 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 (unsigned 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_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_SAMPLERS); + + mPathOperationDirtyObjects.set(State::DIRTY_OBJECT_DRAW_FRAMEBUFFER); + mPathOperationDirtyObjects.set(State::DIRTY_OBJECT_VERTEX_ARRAY); + mPathOperationDirtyObjects.set(State::DIRTY_OBJECT_TEXTURES); + mPathOperationDirtyObjects.set(State::DIRTY_OBJECT_SAMPLERS); + + mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_STATE); + mTexImageDirtyBits.set(State::DIRTY_BIT_UNPACK_BUFFER_BINDING); + // 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); + + // 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); + 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_TEXTURES); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_PROGRAM); + mComputeDirtyObjects.set(State::DIRTY_OBJECT_SAMPLERS); + + mCopyImageDirtyObjects.set(State::DIRTY_OBJECT_READ_FRAMEBUFFER); + + ANGLE_CONTEXT_TRY(mImplementation->initialize()); +} + +egl::Error Context::onDestroy(const egl::Display *display) +{ + if (mGLES1Renderer) + { + mGLES1Renderer->onDestroy(this, &mState); + } + + ANGLE_TRY(unMakeCurrent(display)); + + for (auto fence : mFenceNVMap) + { + 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); + mState.mShaderProgramManager->release(this); + mState.mTextureManager->release(this); + mState.mRenderbufferManager->release(this); + mState.mSamplerManager->release(this); + mState.mSyncManager->release(this); + mState.mPathManager->release(this); + mState.mFramebufferManager->release(this); + mState.mProgramPipelineManager->release(this); + mState.mMemoryObjectManager->release(this); + mState.mSemaphoreManager->release(this); + + mThreadPool.reset(); + + mImplementation->onDestroy(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 *surface) +{ + mDisplay = display; + + if (!mHasBeenCurrent) + { + initialize(); + initRendererString(); + initVersionStrings(); + initExtensionStrings(); + + int width = 0; + int height = 0; + if (surface != nullptr) + { + width = surface->getWidth(); + height = surface->getHeight(); + } + + mState.setViewportParams(0, 0, width, height); + mState.setScissorParams(0, 0, width, height); + + mHasBeenCurrent = true; + } + + // TODO(jmadill): Rework this when we support ContextImpl + mState.setAllDirtyBits(); + mState.setAllDirtyObjects(); + + ANGLE_TRY(setDefaultFramebuffer(surface)); + + // 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(unsetDefaultFramebuffer()); + + return angle::ResultToEGL(mImplementation->onUnMakeCurrent(this)); +} + +GLuint Context::createBuffer() +{ + return mState.mBufferManager->createBuffer(); +} + +GLuint Context::createProgram() +{ + return mState.mShaderProgramManager->createProgram(mImplementation.get()); +} + +GLuint Context::createShader(ShaderType type) +{ + return mState.mShaderProgramManager->createShader(mImplementation.get(), mState.mLimitations, + type); +} + +GLuint Context::createTexture() +{ + return mState.mTextureManager->createTexture(); +} + +GLuint Context::createRenderbuffer() +{ + return mState.mRenderbufferManager->createRenderbuffer(); +} + +void Context::tryGenPaths(GLsizei range, GLuint *createdOut) +{ + ANGLE_CONTEXT_TRY(mState.mPathManager->createPaths(this, range, createdOut)); +} + +GLuint Context::genPaths(GLsizei range) +{ + GLuint created = 0; + tryGenPaths(range, &created); + return created; +} + +// Returns an unused framebuffer name +GLuint Context::createFramebuffer() +{ + return mState.mFramebufferManager->createFramebuffer(); +} + +void Context::genFencesNV(GLsizei n, GLuint *fences) +{ + for (int i = 0; i < n; i++) + { + GLuint handle = mFenceNVHandleAllocator.allocate(); + mFenceNVMap.assign(handle, new FenceNV(mImplementation->createFenceNV())); + fences[i] = handle; + } +} + +GLuint Context::createProgramPipeline() +{ + return mState.mProgramPipelineManager->createProgramPipeline(); +} + +GLuint Context::createShaderProgramv(ShaderType type, GLsizei count, const GLchar *const *strings) +{ + UNIMPLEMENTED(); + return 0u; +} + +GLuint Context::createMemoryObject() +{ + return mState.mMemoryObjectManager->createMemoryObject(mImplementation.get()); +} + +GLuint Context::createSemaphore() +{ + return mState.mSemaphoreManager->createSemaphore(mImplementation.get()); +} + +void Context::deleteBuffer(GLuint bufferName) +{ + Buffer *buffer = mState.mBufferManager->getBuffer(bufferName); + if (buffer) + { + detachBuffer(buffer); + } + + mState.mBufferManager->deleteObject(this, bufferName); +} + +void Context::deleteShader(GLuint shader) +{ + mState.mShaderProgramManager->deleteShader(this, shader); +} + +void Context::deleteProgram(GLuint program) +{ + mState.mShaderProgramManager->deleteProgram(this, program); +} + +void Context::deleteTexture(GLuint texture) +{ + if (mState.mTextureManager->getTexture(texture)) + { + detachTexture(texture); + } + + mState.mTextureManager->deleteObject(this, texture); +} + +void Context::deleteRenderbuffer(GLuint 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(GLuint pipeline) +{ + if (mState.mProgramPipelineManager->getProgramPipeline(pipeline)) + { + detachProgramPipeline(pipeline); + } + + mState.mProgramPipelineManager->deleteObject(this, pipeline); +} + +void Context::deleteMemoryObject(GLuint memoryObject) +{ + mState.mMemoryObjectManager->deleteMemoryObject(this, memoryObject); +} + +void Context::deleteSemaphore(GLuint semaphore) +{ + mState.mSemaphoreManager->deleteSemaphore(this, semaphore); +} + +void Context::deletePaths(GLuint first, GLsizei range) +{ + mState.mPathManager->deletePaths(first, range); +} + +GLboolean Context::isPath(GLuint path) +{ + const auto *pathObj = mState.mPathManager->getPath(path); + if (pathObj == nullptr) + return false; + + return pathObj->hasPathData(); +} + +bool Context::isPathGenerated(GLuint path) const +{ + return mState.mPathManager->hasPath(path); +} + +void Context::pathCommands(GLuint path, + GLsizei numCommands, + const GLubyte *commands, + GLsizei numCoords, + GLenum coordType, + const void *coords) +{ + auto *pathObject = mState.mPathManager->getPath(path); + + ANGLE_CONTEXT_TRY(pathObject->setCommands(numCommands, commands, numCoords, coordType, coords)); +} + +void Context::pathParameterf(GLuint path, GLenum pname, GLfloat value) +{ + Path *pathObj = mState.mPathManager->getPath(path); + + switch (pname) + { + case GL_PATH_STROKE_WIDTH_CHROMIUM: + pathObj->setStrokeWidth(value); + break; + case GL_PATH_END_CAPS_CHROMIUM: + pathObj->setEndCaps(static_cast<GLenum>(value)); + break; + case GL_PATH_JOIN_STYLE_CHROMIUM: + pathObj->setJoinStyle(static_cast<GLenum>(value)); + break; + case GL_PATH_MITER_LIMIT_CHROMIUM: + pathObj->setMiterLimit(value); + break; + case GL_PATH_STROKE_BOUND_CHROMIUM: + pathObj->setStrokeBound(value); + break; + default: + UNREACHABLE(); + break; + } +} + +void Context::pathParameteri(GLuint path, GLenum pname, GLint value) +{ + // TODO(jmadill): Should use proper clamping/casting. + pathParameterf(path, pname, static_cast<GLfloat>(value)); +} + +void Context::getPathParameterfv(GLuint path, GLenum pname, GLfloat *value) +{ + const Path *pathObj = mState.mPathManager->getPath(path); + + switch (pname) + { + case GL_PATH_STROKE_WIDTH_CHROMIUM: + *value = pathObj->getStrokeWidth(); + break; + case GL_PATH_END_CAPS_CHROMIUM: + *value = static_cast<GLfloat>(pathObj->getEndCaps()); + break; + case GL_PATH_JOIN_STYLE_CHROMIUM: + *value = static_cast<GLfloat>(pathObj->getJoinStyle()); + break; + case GL_PATH_MITER_LIMIT_CHROMIUM: + *value = pathObj->getMiterLimit(); + break; + case GL_PATH_STROKE_BOUND_CHROMIUM: + *value = pathObj->getStrokeBound(); + break; + default: + UNREACHABLE(); + break; + } +} + +void Context::getPathParameteriv(GLuint path, GLenum pname, GLint *value) +{ + GLfloat val = 0.0f; + getPathParameterfv(path, pname, value != nullptr ? &val : nullptr); + if (value) + *value = static_cast<GLint>(val); +} + +void Context::pathStencilFunc(GLenum func, GLint ref, GLuint mask) +{ + mState.setPathStencilFunc(func, ref, mask); +} + +// 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(GLuint framebuffer) +{ + if (mState.mFramebufferManager->getFramebuffer(framebuffer)) + { + detachFramebuffer(framebuffer); + } + + mState.mFramebufferManager->deleteObject(this, framebuffer); +} + +void Context::deleteFencesNV(GLsizei n, const GLuint *fences) +{ + for (int i = 0; i < n; i++) + { + GLuint fence = fences[i]; + + FenceNV *fenceObject = nullptr; + if (mFenceNVMap.erase(fence, &fenceObject)) + { + mFenceNVHandleAllocator.release(fence); + delete fenceObject; + } + } +} + +Buffer *Context::getBuffer(GLuint handle) const +{ + return mState.mBufferManager->getBuffer(handle); +} + +Renderbuffer *Context::getRenderbuffer(GLuint handle) const +{ + return mState.mRenderbufferManager->getRenderbuffer(handle); +} + +Sync *Context::getSync(GLsync handle) const +{ + return mState.mSyncManager->getSync(static_cast<GLuint>(reinterpret_cast<uintptr_t>(handle))); +} + +VertexArray *Context::getVertexArray(GLuint handle) const +{ + return mVertexArrayMap.query(handle); +} + +Sampler *Context::getSampler(GLuint handle) const +{ + return mState.mSamplerManager->getSampler(handle); +} + +TransformFeedback *Context::getTransformFeedback(GLuint handle) const +{ + return mTransformFeedbackMap.query(handle); +} + +ProgramPipeline *Context::getProgramPipeline(GLuint handle) const +{ + return mState.mProgramPipelineManager->getProgramPipeline(handle); +} + +gl::LabeledObject *Context::getLabeledObject(GLenum identifier, GLuint name) const +{ + switch (identifier) + { + case GL_BUFFER: + return getBuffer(name); + case GL_SHADER: + return getShader(name); + case GL_PROGRAM: + return getProgramNoResolveLink(name); + case GL_VERTEX_ARRAY: + return getVertexArray(name); + case GL_QUERY: + 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); + 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); + 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::objectPtrLabel(const void *ptr, GLsizei length, const GLchar *label) +{ + gl::LabeledObject *object = getLabeledObjectFromPtr(ptr); + ASSERT(object != nullptr); + + std::string labelName = GetObjectLabelFromPointer(length, label); + 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(GLuint samplerName) +{ + return mState.mSamplerManager->isSampler(samplerName); +} + +void Context::bindTexture(TextureType target, GLuint handle) +{ + Texture *texture = nullptr; + + if (handle == 0) + { + texture = mZeroTextures[target].get(); + } + else + { + texture = + mState.mTextureManager->checkTextureAllocation(mImplementation.get(), handle, target); + } + + ASSERT(texture); + mState.setSamplerTexture(this, target, texture); + mStateCache.onActiveTextureChange(this); +} + +void Context::bindReadFramebuffer(GLuint framebufferHandle) +{ + Framebuffer *framebuffer = mState.mFramebufferManager->checkFramebufferAllocation( + mImplementation.get(), mState.mCaps, framebufferHandle); + mState.setReadFramebufferBinding(framebuffer); + mReadFramebufferObserverBinding.bind(framebuffer); +} + +void Context::bindDrawFramebuffer(GLuint framebufferHandle) +{ + Framebuffer *framebuffer = mState.mFramebufferManager->checkFramebufferAllocation( + mImplementation.get(), mState.mCaps, framebufferHandle); + mState.setDrawFramebufferBinding(framebuffer); + mDrawFramebufferObserverBinding.bind(framebuffer); + mStateCache.onDrawFramebufferChange(this); +} + +void Context::bindVertexArray(GLuint vertexArrayHandle) +{ + VertexArray *vertexArray = checkVertexArrayAllocation(vertexArrayHandle); + mState.setVertexArrayBinding(this, vertexArray); + mVertexArrayObserverBinding.bind(vertexArray); + mStateCache.onVertexArrayBindingChange(this); +} + +void Context::bindVertexBuffer(GLuint bindingIndex, + GLuint 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, GLuint samplerHandle) +{ + ASSERT(textureUnit < mState.mCaps.maxCombinedTextureImageUnits); + Sampler *sampler = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), samplerHandle); + mState.setSamplerBinding(this, textureUnit, sampler); + mSamplerObserverBindings[textureUnit].bind(sampler); + mStateCache.onActiveTextureChange(this); +} + +void Context::bindImageTexture(GLuint unit, + GLuint 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); +} + +void Context::useProgram(GLuint program) +{ + ANGLE_CONTEXT_TRY(mState.setProgram(this, getProgramResolveLink(program))); + mStateCache.onProgramExecutableChange(this); +} + +void Context::useProgramStages(GLuint pipeline, GLbitfield stages, GLuint program) +{ + UNIMPLEMENTED(); +} + +void Context::bindTransformFeedback(GLenum target, GLuint transformFeedbackHandle) +{ + ASSERT(target == GL_TRANSFORM_FEEDBACK); + TransformFeedback *transformFeedback = + checkTransformFeedbackAllocation(transformFeedbackHandle); + mState.setTransformFeedbackBinding(this, transformFeedback); +} + +void Context::bindProgramPipeline(GLuint pipelineHandle) +{ + ProgramPipeline *pipeline = mState.mProgramPipelineManager->checkProgramPipelineAllocation( + mImplementation.get(), pipelineHandle); + mState.setProgramPipelineBinding(this, pipeline); +} + +void Context::beginQuery(QueryType target, GLuint query) +{ + Query *queryObject = getQuery(query, true, 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(GLuint id, QueryType target) +{ + ASSERT(target == QueryType::Timestamp); + + Query *queryObject = getQuery(id, true, 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); + break; + case GL_QUERY_COUNTER_BITS_EXT: + switch (target) + { + case QueryType::TimeElapsed: + params[0] = getExtensions().queryCounterBitsTimeElapsed; + break; + case QueryType::Timestamp: + params[0] = getExtensions().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(GLuint id, GLenum pname, GLint *params) +{ + ANGLE_CONTEXT_TRY(GetQueryObjectParameter(this, getQuery(id), pname, params)); +} + +void Context::getQueryObjectivRobust(GLuint id, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getQueryObjectiv(id, pname, params); +} + +void Context::getQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) +{ + ANGLE_CONTEXT_TRY(GetQueryObjectParameter(this, getQuery(id), pname, params)); +} + +void Context::getQueryObjectuivRobust(GLuint id, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + getQueryObjectuiv(id, pname, params); +} + +void Context::getQueryObjecti64v(GLuint id, GLenum pname, GLint64 *params) +{ + ANGLE_CONTEXT_TRY(GetQueryObjectParameter(this, getQuery(id), pname, params)); +} + +void Context::getQueryObjecti64vRobust(GLuint id, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint64 *params) +{ + getQueryObjecti64v(id, pname, params); +} + +void Context::getQueryObjectui64v(GLuint id, GLenum pname, GLuint64 *params) +{ + ANGLE_CONTEXT_TRY(GetQueryObjectParameter(this, getQuery(id), pname, params)); +} + +void Context::getQueryObjectui64vRobust(GLuint id, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLuint64 *params) +{ + getQueryObjectui64v(id, pname, params); +} + +Framebuffer *Context::getFramebuffer(GLuint handle) const +{ + return mState.mFramebufferManager->getFramebuffer(handle); +} + +FenceNV *Context::getFenceNV(GLuint handle) +{ + return mFenceNVMap.query(handle); +} + +Query *Context::getQuery(GLuint handle, bool create, QueryType type) +{ + if (!mQueryMap.contains(handle)) + { + return nullptr; + } + + Query *query = mQueryMap.query(handle); + if (!query && create) + { + ASSERT(type != QueryType::InvalidEnum); + query = new Query(mImplementation->createQuery(type), handle); + query->addRef(); + mQueryMap.assign(handle, query); + } + return query; +} + +Query *Context::getQuery(GLuint 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)); + } + return mCompiler.get(); +} + +void Context::getBooleanvImpl(GLenum pname, GLboolean *params) +{ + switch (pname) + { + case GL_SHADER_COMPILER: + *params = GL_TRUE; + break; + case GL_CONTEXT_ROBUST_ACCESS_EXT: + *params = ConvertToGLBoolean(mRobustAccess); + break; + + default: + mState.getBooleanv(pname, params); + break; + } +} + +void Context::getFloatvImpl(GLenum pname, GLfloat *params) +{ + // 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.textureFilterAnisotropic); + *params = mState.mExtensions.maxTextureAnisotropy; + break; + case GL_MAX_TEXTURE_LOD_BIAS: + *params = mState.mCaps.maxLODBias; + break; + + case GL_PATH_MODELVIEW_MATRIX_CHROMIUM: + case GL_PATH_PROJECTION_MATRIX_CHROMIUM: + { + // GLES1 emulation: // GL_PATH_(MODELVIEW|PROJECTION)_MATRIX_CHROMIUM collides with the + // GLES1 constants for modelview/projection matrix. + if (getClientVersion() < Version(2, 0)) + { + mState.getFloatv(pname, params); + } + else + { + ASSERT(mState.mExtensions.pathRendering); + const GLfloat *m = mState.getPathRenderingMatrix(pname); + memcpy(params, m, 16 * sizeof(GLfloat)); + } + } + break; + + default: + mState.getFloatv(pname, params); + break; + } +} + +void Context::getIntegervImpl(GLenum pname, GLint *params) +{ + // 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.maxVertexOutputComponents; + 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; + + // 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.mExtensions.maxDebugMessageLength; + break; + case GL_MAX_DEBUG_LOGGED_MESSAGES: + *params = mState.mExtensions.maxDebugLoggedMessages; + break; + case GL_MAX_DEBUG_GROUP_STACK_DEPTH: + *params = mState.mExtensions.maxDebugGroupStackDepth; + break; + case GL_MAX_LABEL_LENGTH: + *params = mState.mExtensions.maxLabelLength; + break; + + // GL_OVR_multiview2 + case GL_MAX_VIEWS_OVR: + *params = mState.mExtensions.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; + // 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_PLANES: + *params = mState.mCaps.maxClipPlanes; + 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: + getVertexAttribiv(static_cast<GLuint>(vertexArrayIndex(ParamToVertexArrayType(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: + getVertexAttribiv(static_cast<GLuint>(vertexArrayIndex(ParamToVertexArrayType(pname))), + GL_VERTEX_ATTRIB_ARRAY_STRIDE, params); + break; + case GL_VERTEX_ARRAY_SIZE: + case GL_COLOR_ARRAY_SIZE: + case GL_TEXTURE_COORD_ARRAY_SIZE: + getVertexAttribiv(static_cast<GLuint>(vertexArrayIndex(ParamToVertexArrayType(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: + getVertexAttribiv(static_cast<GLuint>(vertexArrayIndex(ParamToVertexArrayType(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.mExtensions.maxDualSourceDrawBuffers; + break; + + default: + ANGLE_CONTEXT_TRY(mState.getIntegerv(this, pname, params)); + break; + } +} + +void Context::getInteger64vImpl(GLenum pname, GLint64 *params) +{ + // 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) const +{ + 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(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::getTexParameterfv(TextureType target, GLenum pname, GLfloat *params) +{ + const Texture *const texture = getTextureByType(target); + QueryTexParameterfv(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(texture, pname, params); +} + +void Context::getTexParameterIiv(TextureType target, GLenum pname, GLint *params) +{ + const Texture *const texture = getTextureByType(target); + QueryTexParameterIiv(texture, pname, params); +} + +void Context::getTexParameterIuiv(TextureType target, GLenum pname, GLuint *params) +{ + const Texture *const texture = getTextureByType(target); + QueryTexParameterIuiv(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) +{ + 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)) + { + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->drawArraysInstanced(this, mode, first, count, instanceCount)); + MarkTransformFeedbackBufferUsage(this, count, instanceCount); +} + +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)) + { + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->drawElementsInstanced(this, mode, count, type, indices, instances)); +} + +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)) + { + return; + } + + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY( + mImplementation->drawRangeElements(this, mode, start, end, count, type, indices)); +} + +void Context::drawArraysIndirect(PrimitiveMode mode, const void *indirect) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->drawArraysIndirect(this, mode, indirect)); +} + +void Context::drawElementsIndirect(PrimitiveMode mode, DrawElementsType type, const void *indirect) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + ANGLE_CONTEXT_TRY(mImplementation->drawElementsIndirect(this, mode, type, indirect)); +} + +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); + 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." + mImplementation->pushGroupMarker(length, ""); + } + else + { + mImplementation->pushGroupMarker(length, marker); + } +} + +void Context::popGroupMarker() +{ + ASSERT(mImplementation); + mImplementation->popGroupMarker(); +} + +void Context::bindUniformLocation(GLuint program, GLint location, const GLchar *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + + programObject->bindUniformLocation(location, name); +} + +void Context::coverageModulation(GLenum components) +{ + mState.setCoverageModulation(components); +} + +void Context::matrixLoadf(GLenum matrixMode, const GLfloat *matrix) +{ + mState.loadPathRenderingMatrix(matrixMode, matrix); +} + +void Context::matrixLoadIdentity(GLenum matrixMode) +{ + GLfloat I[16]; + angle::Matrix<GLfloat>::setToIdentity(I); + + mState.loadPathRenderingMatrix(matrixMode, I); +} + +void Context::stencilFillPath(GLuint path, GLenum fillMode, GLuint mask) +{ + const auto *pathObj = mState.mPathManager->getPath(path); + if (!pathObj) + return; + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->stencilFillPath(pathObj, fillMode, mask); +} + +void Context::stencilStrokePath(GLuint path, GLint reference, GLuint mask) +{ + const auto *pathObj = mState.mPathManager->getPath(path); + if (!pathObj) + return; + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->stencilStrokePath(pathObj, reference, mask); +} + +void Context::coverFillPath(GLuint path, GLenum coverMode) +{ + const auto *pathObj = mState.mPathManager->getPath(path); + if (!pathObj) + return; + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->coverFillPath(pathObj, coverMode); +} + +void Context::coverStrokePath(GLuint path, GLenum coverMode) +{ + const auto *pathObj = mState.mPathManager->getPath(path); + if (!pathObj) + return; + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->coverStrokePath(pathObj, coverMode); +} + +void Context::stencilThenCoverFillPath(GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode) +{ + const auto *pathObj = mState.mPathManager->getPath(path); + if (!pathObj) + return; + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->stencilThenCoverFillPath(pathObj, fillMode, mask, coverMode); +} + +void Context::stencilThenCoverStrokePath(GLuint path, + GLint reference, + GLuint mask, + GLenum coverMode) +{ + const auto *pathObj = mState.mPathManager->getPath(path); + if (!pathObj) + return; + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->stencilThenCoverStrokePath(pathObj, reference, mask, coverMode); +} + +void Context::coverFillPathInstanced(GLsizei numPaths, + GLenum pathNameType, + const void *paths, + GLuint pathBase, + GLenum coverMode, + GLenum transformType, + const GLfloat *transformValues) +{ + const auto &pathObjects = + GatherPaths(*mState.mPathManager, numPaths, pathNameType, paths, pathBase); + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->coverFillPathInstanced(pathObjects, coverMode, transformType, transformValues); +} + +void Context::coverStrokePathInstanced(GLsizei numPaths, + GLenum pathNameType, + const void *paths, + GLuint pathBase, + GLenum coverMode, + GLenum transformType, + const GLfloat *transformValues) +{ + const auto &pathObjects = + GatherPaths(*mState.mPathManager, numPaths, pathNameType, paths, pathBase); + + // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering? + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->coverStrokePathInstanced(pathObjects, coverMode, transformType, + transformValues); +} + +void Context::stencilFillPathInstanced(GLsizei numPaths, + GLenum pathNameType, + const void *paths, + GLuint pathBase, + GLenum fillMode, + GLuint mask, + GLenum transformType, + const GLfloat *transformValues) +{ + const auto &pathObjects = + GatherPaths(*mState.mPathManager, numPaths, pathNameType, paths, pathBase); + + // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering? + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->stencilFillPathInstanced(pathObjects, fillMode, mask, transformType, + transformValues); +} + +void Context::stencilStrokePathInstanced(GLsizei numPaths, + GLenum pathNameType, + const void *paths, + GLuint pathBase, + GLint reference, + GLuint mask, + GLenum transformType, + const GLfloat *transformValues) +{ + const auto &pathObjects = + GatherPaths(*mState.mPathManager, numPaths, pathNameType, paths, pathBase); + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->stencilStrokePathInstanced(pathObjects, reference, mask, transformType, + transformValues); +} + +void Context::stencilThenCoverFillPathInstanced(GLsizei numPaths, + GLenum pathNameType, + const void *paths, + GLuint pathBase, + GLenum fillMode, + GLuint mask, + GLenum coverMode, + GLenum transformType, + const GLfloat *transformValues) +{ + const auto &pathObjects = + GatherPaths(*mState.mPathManager, numPaths, pathNameType, paths, pathBase); + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->stencilThenCoverFillPathInstanced(pathObjects, coverMode, fillMode, mask, + transformType, transformValues); +} + +void Context::stencilThenCoverStrokePathInstanced(GLsizei numPaths, + GLenum pathNameType, + const void *paths, + GLuint pathBase, + GLint reference, + GLuint mask, + GLenum coverMode, + GLenum transformType, + const GLfloat *transformValues) +{ + const auto &pathObjects = + GatherPaths(*mState.mPathManager, numPaths, pathNameType, paths, pathBase); + + ANGLE_CONTEXT_TRY(syncStateForPathOperation()); + + mImplementation->stencilThenCoverStrokePathInstanced(pathObjects, coverMode, reference, mask, + transformType, transformValues); +} + +void Context::bindFragmentInputLocation(GLuint program, GLint location, const GLchar *name) +{ + auto *programObject = getProgramResolveLink(program); + + programObject->bindFragmentInputLocation(location, name); +} + +void Context::programPathFragmentInputGen(GLuint program, + GLint location, + GLenum genMode, + GLint components, + const GLfloat *coeffs) +{ + auto *programObject = getProgramResolveLink(program); + + programObject->pathFragmentInputGen(location, genMode, components, coeffs); +} + +GLuint Context::getProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar *name) +{ + const Program *programObject = getProgramResolveLink(program); + return QueryProgramResourceIndex(programObject, programInterface, name); +} + +void Context::getProgramResourceName(GLuint program, + GLenum programInterface, + GLuint index, + GLsizei bufSize, + GLsizei *length, + GLchar *name) +{ + const Program *programObject = getProgramResolveLink(program); + QueryProgramResourceName(programObject, programInterface, index, bufSize, length, name); +} + +GLint Context::getProgramResourceLocation(GLuint program, + GLenum programInterface, + const GLchar *name) +{ + const Program *programObject = getProgramResolveLink(program); + return QueryProgramResourceLocation(programObject, programInterface, name); +} + +void Context::getProgramResourceiv(GLuint 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(GLuint program, + GLenum programInterface, + GLenum pname, + GLint *params) +{ + const Program *programObject = getProgramResolveLink(program); + QueryProgramInterfaceiv(programObject, programInterface, pname, params); +} + +void Context::getProgramInterfaceivRobust(GLuint 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(GLenum errorCode, const char *message) +{ + mErrors.validationError(errorCode, message); +} + +// 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; + } + mContextLost = true; +} + +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 (!mContextLost && mImplementation->getResetStatus() != GraphicsResetStatus::NoError) + { + mContextLost = true; + } + + // 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 (!mContextLost) + { + ASSERT(mResetStatus == GraphicsResetStatus::NoError); + mResetStatus = mImplementation->getResetStatus(); + + if (mResetStatus != GraphicsResetStatus::NoError) + { + mContextLost = true; + } + } + 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() +{ + return (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT); +} + +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(0); + if (framebuffer == nullptr) + { + return EGL_NONE; + } + + const FramebufferAttachment *backAttachment = framebuffer->getAttachment(this, GL_BACK); + ASSERT(backAttachment != nullptr); + return backAttachment->getSurface()->getRenderBuffer(); +} + +VertexArray *Context::checkVertexArrayAllocation(GLuint 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(GLuint 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(GLuint vertexArray) +{ + ASSERT(mVertexArrayMap.contains(0)); + return mVertexArrayMap.contains(vertexArray); +} + +bool Context::isTransformFeedbackGenerated(GLuint transformFeedback) +{ + ASSERT(mTransformFeedbackMap.contains(0)); + return mTransformFeedbackMap.contains(transformFeedback); +} + +void Context::detachTexture(GLuint texture) +{ + // 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(GLuint 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 != 0) + { + bindReadFramebuffer(0); + } + + if (mState.removeDrawFramebufferBinding(framebuffer) && framebuffer != 0) + { + bindDrawFramebuffer(0); + } +} + +void Context::detachRenderbuffer(GLuint renderbuffer) +{ + mState.detachRenderbuffer(this, renderbuffer); +} + +void Context::detachVertexArray(GLuint 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(GLuint 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); + } +} + +void Context::detachSampler(GLuint sampler) +{ + mState.detachSampler(this, sampler); +} + +void Context::detachProgramPipeline(GLuint pipeline) +{ + mState.detachProgramPipeline(this, pipeline); +} + +void Context::vertexAttribDivisor(GLuint index, GLuint divisor) +{ + mState.setVertexAttribDivisor(this, index, divisor); + mStateCache.onVertexArrayStateChange(this); +} + +void Context::samplerParameteri(GLuint sampler, GLenum pname, GLint param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameteri(this, samplerObject, pname, param); +} + +void Context::samplerParameteriv(GLuint sampler, GLenum pname, const GLint *param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameteriv(this, samplerObject, pname, param); +} + +void Context::samplerParameterIiv(GLuint sampler, GLenum pname, const GLint *param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameterIiv(this, samplerObject, pname, param); +} + +void Context::samplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint *param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameterIuiv(this, samplerObject, pname, param); +} + +void Context::samplerParameterivRobust(GLuint sampler, + GLenum pname, + GLsizei bufSize, + const GLint *param) +{ + samplerParameteriv(sampler, pname, param); +} + +void Context::samplerParameterIivRobust(GLuint sampler, + GLenum pname, + GLsizei bufSize, + const GLint *param) +{ + UNIMPLEMENTED(); +} + +void Context::samplerParameterIuivRobust(GLuint sampler, + GLenum pname, + GLsizei bufSize, + const GLuint *param) +{ + UNIMPLEMENTED(); +} + +void Context::samplerParameterf(GLuint sampler, GLenum pname, GLfloat param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameterf(this, samplerObject, pname, param); +} + +void Context::samplerParameterfv(GLuint sampler, GLenum pname, const GLfloat *param) +{ + Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + SetSamplerParameterfv(this, samplerObject, pname, param); +} + +void Context::samplerParameterfvRobust(GLuint sampler, + GLenum pname, + GLsizei bufSize, + const GLfloat *param) +{ + samplerParameterfv(sampler, pname, param); +} + +void Context::getSamplerParameteriv(GLuint sampler, GLenum pname, GLint *params) +{ + const Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + QuerySamplerParameteriv(samplerObject, pname, params); +} + +void Context::getSamplerParameterIiv(GLuint sampler, GLenum pname, GLint *params) +{ + const Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + QuerySamplerParameterIiv(samplerObject, pname, params); +} + +void Context::getSamplerParameterIuiv(GLuint sampler, GLenum pname, GLuint *params) +{ + const Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + QuerySamplerParameterIuiv(samplerObject, pname, params); +} + +void Context::getSamplerParameterivRobust(GLuint sampler, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getSamplerParameteriv(sampler, pname, params); +} + +void Context::getSamplerParameterIivRobust(GLuint sampler, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getSamplerParameterIuivRobust(GLuint sampler, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat *params) +{ + const Sampler *const samplerObject = + mState.mSamplerManager->checkSamplerAllocation(mImplementation.get(), sampler); + QuerySamplerParameterfv(samplerObject, pname, params); +} + +void Context::getSamplerParameterfvRobust(GLuint sampler, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + getSamplerParameterfv(sampler, pname, params); +} + +void Context::programParameteri(GLuint program, GLenum pname, GLint value) +{ + gl::Program *programObject = getProgramResolveLink(program); + SetProgramParameteri(programObject, pname, value); +} + +void Context::initRendererString() +{ + std::ostringstream rendererString; + rendererString << "ANGLE ("; + rendererString << mImplementation->getRendererDescription(); + rendererString << ")"; + + mRendererString = MakeStaticString(rendererString.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_VERSION_STRING << ")"; + 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_VERSION_STRING + << ")"; + 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 *>("Google Inc."); + + 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); + + 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 ExtensionInfoMap &extensionInfos = GetExtensionInfoMap(); + auto extension = extensionInfos.find(name); + + return extension != extensionInfos.end() && extension->second.Requestable && + mSupportedExtensions.*(extension->second.ExtensionsMember); +} + +void Context::requestExtension(const char *name) +{ + 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)) + { + // Extension already enabled + return; + } + + mState.mExtensions.*(extension.ExtensionsMember) = true; + 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->invalidateFramebufferComplenessCache(); +} + +size_t Context::getRequestableExtensionStringCount() const +{ + return mRequestableExtensionStrings.size(); +} + +void Context::beginTransformFeedback(PrimitiveMode primitiveMode) +{ + TransformFeedback *transformFeedback = mState.getCurrentTransformFeedback(); + ASSERT(transformFeedback != nullptr); + ASSERT(!transformFeedback->isPaused()); + + ANGLE_CONTEXT_TRY(transformFeedback->begin(this, primitiveMode, mState.getProgram())); + mStateCache.onActiveTransformFeedbackChange(this); +} + +bool Context::hasActiveTransformFeedback(GLuint 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(); + + // Explicitly enable GL_KHR_parallel_shader_compile + supportedExtensions.parallelShaderCompile = true; + + if (getClientVersion() < ES_2_0) + { + // Default extensions for GLES1 + supportedExtensions.pointSizeArray = true; + supportedExtensions.textureCubeMap = true; + supportedExtensions.pointSprite = true; + supportedExtensions.drawTexture = true; + supportedExtensions.parallelShaderCompile = false; + supportedExtensions.texture3DOES = false; + } + + if (getClientVersion() < ES_3_0) + { + // Disable ES3+ extensions + supportedExtensions.colorBufferFloat = false; + supportedExtensions.eglImageExternalEssl3 = false; + supportedExtensions.textureNorm16 = false; + supportedExtensions.multiview = false; + supportedExtensions.multiview2 = false; + supportedExtensions.maxViews = 1u; + supportedExtensions.copyTexture3d = false; + supportedExtensions.textureMultisample = false; + + // Requires glCompressedTexImage3D + supportedExtensions.textureCompressionASTCOES = false; + + // Don't expose GL_EXT_texture_sRGB_decode without sRGB texture support + if (!supportedExtensions.sRGB) + { + supportedExtensions.textureSRGBDecode = false; + } + } + + if (getClientVersion() < ES_3_1) + { + // Disable ES3.1+ extensions + supportedExtensions.geometryShader = 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.textureStorageMultisample2DArray = false; + } + + if (getClientVersion() > ES_2_0) + { + // FIXME(geofflang): Don't support EXT_sRGB in non-ES2 contexts + // supportedExtensions.sRGB = false; + } + + // Some extensions are always available because they are implemented in the GL layer. + supportedExtensions.bindUniformLocation = true; + supportedExtensions.vertexArrayObject = true; + supportedExtensions.bindGeneratesResource = true; + supportedExtensions.clientArrays = true; + supportedExtensions.requestExtension = true; + supportedExtensions.multiDraw = true; + + // Enable the no error extension if the context was created with the flag. + supportedExtensions.noError = mSkipValidation; + + // Enable surfaceless to advertise we'll have the correct behavior when there is no default FBO + supportedExtensions.surfacelessContext = mSurfacelessSupported; + + // Explicitly enable GL_KHR_debug + supportedExtensions.debug = true; + supportedExtensions.maxDebugMessageLength = 1024; + supportedExtensions.maxDebugLoggedMessages = 1024; + supportedExtensions.maxDebugGroupStackDepth = 1024; + supportedExtensions.maxLabelLength = 1024; + + // Explicitly enable GL_ANGLE_robust_client_memory + supportedExtensions.robustClientMemory = true; + + // Determine robust resource init availability from EGL. + supportedExtensions.robustResourceInitialization = mState.isRobustResourceInitEnabled(); + + // mState.mExtensions.robustBufferAccessBehavior is true only if robust access is true and the + // backend supports it. + supportedExtensions.robustBufferAccessBehavior = + mRobustAccess && supportedExtensions.robustBufferAccessBehavior; + + // Enable the cache control query unconditionally. + supportedExtensions.programCacheControl = true; + + // Enable EGL_ANGLE_explicit_context subextensions + if (mExplicitContextAvailable) + { + // GL_ANGLE_explicit_context_gles1 + supportedExtensions.explicitContextGles1 = true; + // GL_ANGLE_explicit_context + supportedExtensions.explicitContext = true; + } + + // If EGL_KHR_fence_sync is not enabled, don't expose GL_OES_EGL_sync. + ASSERT(mDisplay); + if (!mDisplay->getExtensions().fenceSync) + { + supportedExtensions.eglSync = false; + } + + supportedExtensions.memorySize = true; + + // GL_CHROMIUM_lose_context is implemented in the frontend + supportedExtensions.loseContextCHROMIUM = true; + + // The ASTC texture extensions have dependency requirements. + if (supportedExtensions.textureCompressionASTCHDRKHR) + { + // 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); + } + + return supportedExtensions; +} + +void Context::initCaps() +{ + mState.mCaps = mImplementation->getNativeCaps(); + + mSupportedExtensions = generateSupportedExtensions(); + mState.mExtensions = mSupportedExtensions; + + mState.mLimitations = mImplementation->getNativeLimitations(); + + // GLES1 emulation: Initialize caps (Table 6.20 / 6.22 in the ES 1.1 spec) + if (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; + } + + // Apply/Verify implementation limits + LimitCap(&mState.mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS); + + ASSERT(mState.mCaps.minAliasedPointSize >= 1.0f); + + if (getClientVersion() < ES_3_1) + { + mState.mCaps.maxVertexAttribBindings = mState.mCaps.maxVertexAttributes; + } + else + { + LimitCap(&mState.mCaps.maxVertexAttribBindings, MAX_VERTEX_ATTRIB_BINDINGS); + } + + LimitCap(&mState.mCaps.maxShaderUniformBlocks[ShaderType::Vertex], + IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS); + LimitCap(&mState.mCaps.maxUniformBufferBindings, IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS); + + LimitCap(&mState.mCaps.maxVertexOutputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4); + LimitCap(&mState.mCaps.maxFragmentInputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4); + + LimitCap(&mState.mCaps.maxTransformFeedbackInterleavedComponents, + IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS); + LimitCap(&mState.mCaps.maxTransformFeedbackSeparateAttributes, + IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS); + LimitCap(&mState.mCaps.maxTransformFeedbackSeparateComponents, + IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS); + + // Limit textures as well, so we can use fast bitsets with texture bindings. + LimitCap(&mState.mCaps.maxCombinedTextureImageUnits, IMPLEMENTATION_MAX_ACTIVE_TEXTURES); + for (ShaderType shaderType : AllShaderTypes()) + { + LimitCap(&mState.mCaps.maxShaderTextureImageUnits[shaderType], + IMPLEMENTATION_MAX_SHADER_TEXTURES); + } + + LimitCap(&mState.mCaps.maxImageUnits, IMPLEMENTATION_MAX_IMAGE_UNITS); + + for (ShaderType shaderType : AllShaderTypes()) + { + LimitCap(&mState.mCaps.maxShaderAtomicCounterBuffers[shaderType], + IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS); + } + LimitCap(&mState.mCaps.maxCombinedAtomicCounterBuffers, + IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS); + + for (ShaderType shaderType : AllShaderTypes()) + { + LimitCap(&mState.mCaps.maxShaderStorageBlocks[shaderType], + IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS); + } + LimitCap(&mState.mCaps.maxShaderStorageBufferBindings, + IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS); + LimitCap(&mState.mCaps.maxCombinedShaderStorageBlocks, + IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS); + + mState.mCaps.maxSampleMaskWords = + std::min<GLuint>(mState.mCaps.maxSampleMaskWords, MAX_SAMPLE_MASK_WORDS); + + // WebGL compatibility + mState.mExtensions.webglCompatibility = 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; + } + } + + // 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); + + // 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 && !mSupportedExtensions.textureMultisample && + 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(mState.mCaps.maxSamples, formatMaxSamples); + } + + // Handle GLES 3.1 MAX_*_SAMPLES values similarly to MAX_SAMPLES. + if (getClientVersion() >= ES_3_1 || mSupportedExtensions.textureMultisample) + { + // 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(mState.mCaps.maxIntegerSamples, formatMaxSamples); + } + + // GLES 3.1 section 19.3.1. + if (formatCaps.texturable) + { + if (formatInfo.depthBits > 0) + { + mState.mCaps.maxDepthTextureSamples = + std::min(mState.mCaps.maxDepthTextureSamples, formatMaxSamples); + } + else if (formatInfo.redBits > 0) + { + mState.mCaps.maxColorTextureSamples = + std::min(mState.mCaps.maxColorTextureSamples, formatMaxSamples); + } + } + } + } + + if (formatCaps.texturable && formatInfo.compressed) + { + mState.mCaps.compressedTextureFormats.push_back(sizedInternalFormat); + } + + mState.mTextureCaps.insert(sizedInternalFormat, formatCaps); + } + + // If program binary is disabled, blank out the memory cache pointer. + if (!mSupportedExtensions.getProgramBinary) + { + mMemoryProgramCache = nullptr; + } + + // Compute which buffer types are allowed + mValidBufferBindings.reset(); + mValidBufferBindings.set(BufferBinding::ElementArray); + mValidBufferBindings.set(BufferBinding::Array); + + if (mState.mExtensions.pixelBufferObject || 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); + } + + mThreadPool = angle::WorkerThreadPool::Create(mState.mExtensions.parallelShaderCompile); + + // 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); + 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.robustBufferAccessBehavior && (mState.isWebGL() || mRobustAccess)); + + // 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) +{ + return (instanceCount == 0) || noopDraw(mode, count); +} + +angle::Result Context::prepareForClear(GLbitfield mask) +{ + // Sync the draw framebuffer manually after the clear attachments. + ASSERT(mClearDirtyObjects.none()); + ANGLE_TRY(mState.getDrawFramebuffer()->ensureClearAttachmentsInitialized(this, mask)); + ANGLE_TRY(mState.syncDirtyObject(this, GL_DRAW_FRAMEBUFFER)); + ANGLE_TRY(syncDirtyBits(mClearDirtyBits)); + return angle::Result::Continue; +} + +angle::Result Context::prepareForClearBuffer(GLenum buffer, GLint drawbuffer) +{ + // Sync the draw framebuffer manually after the clear attachments. + ASSERT(mClearDirtyObjects.none()); + ANGLE_TRY(mState.getDrawFramebuffer()->ensureClearBufferAttachmentsInitialized(this, buffer, + drawbuffer)); + ANGLE_TRY(mState.syncDirtyObject(this, GL_DRAW_FRAMEBUFFER)); + ANGLE_TRY(syncDirtyBits(mClearDirtyBits)); + return angle::Result::Continue; +} + +ANGLE_INLINE angle::Result Context::prepareForDispatch() +{ + ANGLE_TRY(syncDirtyObjects(mComputeDirtyObjects)); + return syncDirtyBits(mComputeDirtyBits); +} + +angle::Result Context::syncState(const State::DirtyBits &bitMask, + const State::DirtyObjects &objectMask) +{ + ANGLE_TRY(syncDirtyObjects(objectMask)); + ANGLE_TRY(syncDirtyBits(bitMask)); + 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); + + 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()); + + ANGLE_CONTEXT_TRY(drawFramebuffer->blit(this, srcArea, dstArea, mask, filter)); +} + +void Context::clear(GLbitfield mask) +{ + ANGLE_CONTEXT_TRY(prepareForClear(mask)); + ANGLE_CONTEXT_TRY(mState.getDrawFramebuffer()->clear(this, mask)); +} + +void Context::clearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *values) +{ + Framebuffer *framebufferObject = mState.getDrawFramebuffer(); + const FramebufferAttachment *attachment = nullptr; + if (buffer == GL_DEPTH) + { + attachment = framebufferObject->getDepthAttachment(); + } + 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) +{ + 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) +{ + Framebuffer *framebufferObject = mState.getDrawFramebuffer(); + const FramebufferAttachment *attachment = nullptr; + if (buffer == GL_STENCIL) + { + attachment = framebufferObject->getStencilAttachment(); + } + 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) +{ + 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); + ANGLE_CONTEXT_TRY(readFBO->readPixels(this, area, format, type, 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(mState.syncDirtyObjects(this, mCopyImageDirtyObjects)); + + 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(mState.syncDirtyObjects(this, mCopyImageDirtyObjects)); + + 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(mState.syncDirtyObjects(this, mCopyImageDirtyObjects)); + + 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::framebufferTexture2D(GLenum target, + GLenum attachment, + TextureTarget textarget, + GLuint texture, + GLint level) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture != 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, + GLuint texture, + GLint level, + GLint zoffset) +{ + UNIMPLEMENTED(); +} + +void Context::framebufferRenderbuffer(GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (renderbuffer != 0) + { + Renderbuffer *renderbufferObject = getRenderbuffer(renderbuffer); + + framebuffer->setAttachment(this, GL_RENDERBUFFER, attachment, gl::ImageIndex(), + renderbufferObject); + } + else + { + framebuffer->resetAttachment(this, attachment); + } + + mState.setObjectDirty(target); +} + +void Context::framebufferTextureLayer(GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLint layer) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture != 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, + GLuint texture, + GLint level, + GLint baseViewIndex, + GLsizei numViews) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture != 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, GLuint texture, GLint level) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (texture != 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) +{ + // Only sync the FBO + ANGLE_CONTEXT_TRY(mState.syncDirtyObject(this, target)); + + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + // The specification isn't clear what should be done when the framebuffer isn't complete. + // We leave it up to the framebuffer implementation to decide what to do. + ANGLE_CONTEXT_TRY(framebuffer->discard(this, numAttachments, attachments)); +} + +void Context::invalidateFramebuffer(GLenum target, + GLsizei numAttachments, + const GLenum *attachments) +{ + // Only sync the FBO + ANGLE_CONTEXT_TRY(mState.syncDirtyObject(this, target)); + + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (!framebuffer->isComplete(this)) + { + return; + } + + 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) +{ + // Only sync the FBO + ANGLE_CONTEXT_TRY(mState.syncDirtyObject(this, target)); + + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + + if (!framebuffer->isComplete(this)) + { + return; + } + + Rectangle area(x, y, width, height); + 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()); + + Extents size(width, height, 1); + Texture *texture = getTextureByTarget(target); + ANGLE_CONTEXT_TRY(texture->setImage(this, mState.getUnpackState(), 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()); + + Extents size(width, height, depth); + Texture *texture = getTextureByTarget(target); + ANGLE_CONTEXT_TRY(texture->setImage(this, mState.getUnpackState(), 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); + ANGLE_CONTEXT_TRY(texture->setCompressedImage(this, mState.getUnpackState(), 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); + ANGLE_CONTEXT_TRY(texture->setCompressedImage(this, mState.getUnpackState(), 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); + ANGLE_CONTEXT_TRY(texture->setCompressedSubImage(this, mState.getUnpackState(), 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); + ANGLE_CONTEXT_TRY(texture->setCompressedSubImage(this, mState.getUnpackState(), 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(GLuint sourceId, + GLint sourceLevel, + TextureTarget destTarget, + GLuint 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(GLuint sourceId, + GLint sourceLevel, + TextureTarget destTarget, + GLuint 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(GLuint sourceId, + GLint sourceLevel, + TextureTarget destTarget, + GLuint 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(GLuint sourceId, + GLint sourceLevel, + TextureTarget destTarget, + GLuint 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(GLuint sourceId, GLuint 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); +} + +angle::Result Context::syncStateForTexImage() +{ + return syncState(mTexImageDirtyBits, mTexImageDirtyObjects); +} + +angle::Result Context::syncStateForBlit() +{ + return syncState(mBlitDirtyBits, mBlitDirtyObjects); +} + +angle::Result Context::syncStateForPathOperation() +{ + ANGLE_TRY(syncDirtyObjects(mPathOperationDirtyObjects)); + + // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering? + ANGLE_TRY(syncDirtyBits()); + + return angle::Result::Continue; +} + +void Context::activeShaderProgram(GLuint pipeline, GLuint program) +{ + UNIMPLEMENTED(); +} + +void Context::activeTexture(GLenum texture) +{ + mState.setActiveSampler(texture - GL_TEXTURE0); +} + +void Context::blendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +{ + /* EXT_color_buffer_half_float: + Modify Section 4.1.6 (Blending), p. 96 + + (modify second paragraph, p. 96) [...] + + If the color buffer is fixed-point, the components of the source and + destination values and blend factors are clamped to [0, 1] prior to + evaluating the blend equation. If the color buffer is floating-point, no + clamping occurs. The resulting four values are sent to the next operation. + */ + /* EXT_color_buffer_float: + 4.1.7 Blending, p.177 (section 15.1.7 p.315 in ES 3.1) + + Modify paragraphs 3 & 4: + + * "If the color buffer is fixed-point, the components of the + source and destination values and blend factors are clamped + * to [0; 1] prior to evaluating the blend equation. If the color + + buffer is floating-point, no clamping occurs. The resulting four + + values are sent to the next operation. + */ + // Fortunately, (at least) GL and D3D11 have this same behavior, so there's nothing special we + // need to do there. If any backend /doesn't/ do this (does Vulkan?), it will need to do + // clamping based on the pipeline formats. + const auto &exts = mState.mExtensions; + const bool hasFloatBlending = exts.colorBufferFloat || exts.colorBufferHalfFloat || + exts.colorBufferFloatRGB || exts.colorBufferFloatRGBA; + if (!hasFloatBlending) + { + red = clamp01(red); + green = clamp01(green); + blue = clamp01(blue); + alpha = clamp01(alpha); + } + + mState.setBlendColor(red, green, blue, alpha); +} + +void Context::blendEquation(GLenum mode) +{ + mState.setBlendEquation(mode, mode); +} + +void Context::blendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) +{ + mState.setBlendEquation(modeRGB, modeAlpha); +} + +void Context::blendFunc(GLenum sfactor, GLenum dfactor) +{ + mState.setBlendFactors(sfactor, dfactor, sfactor, dfactor); +} + +void Context::blendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +{ + mState.setBlendFactors(srcRGB, dstRGB, srcAlpha, dstAlpha); +} + +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::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::disable(GLenum cap) +{ + mState.setEnableFeature(cap, false); + 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::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; + 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().unpackSubimage); + 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().unpackSubimage); + mState.setUnpackSkipRows(param); + break; + + case GL_UNPACK_SKIP_PIXELS: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().unpackSubimage); + mState.setUnpackSkipPixels(param); + break; + + case GL_PACK_ROW_LENGTH: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().packSubimage); + mState.setPackRowLength(param); + break; + + case GL_PACK_SKIP_ROWS: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().packSubimage); + mState.setPackSkipRows(param); + break; + + case GL_PACK_SKIP_PIXELS: + ASSERT((getClientMajorVersion() >= 3) || getExtensions().packSubimage); + 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::stencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) +{ + if (face == GL_FRONT || face == GL_FRONT_AND_BACK) + { + mState.setStencilParams(func, ref, mask); + } + + if (face == GL_BACK || face == GL_FRONT_AND_BACK) + { + mState.setStencilBackParams(func, ref, 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(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::getVertexAttribiv(GLuint index, GLenum pname, GLint *params) +{ + const VertexAttribCurrentValueData ¤tValues = + getState().getVertexAttribCurrentValue(index); + const VertexArray *vao = getState().getVertexArray(); + QueryVertexAttribiv(vao->getVertexAttribute(index), vao->getBindingFromAttribIndex(index), + currentValues, 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); +} + +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)); + mImplementation->pushDebugGroup(source, id, msg); + mState.getDebug().pushGroup(source, id, std::move(msg)); +} + +void Context::popDebugGroup() +{ + mState.getDebug().popGroup(); + mImplementation->popDebugGroup(); +} + +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(GLuint program, GLuint 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(GLuint 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, GLuint buffer) +{ + bindBufferRange(target, index, buffer, 0, 0); +} + +void Context::bindBufferRange(BufferBinding target, + GLuint index, + GLuint 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 + { + mStateCache.onBufferBindingChange(this); + } +} + +void Context::bindFramebuffer(GLenum target, GLuint 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, GLuint 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.invalidateTexture(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) +{ + // 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)); +} + +void Context::getSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) +{ + const Sync *syncObject = nullptr; + if (!mContextLost) + { + 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 +{ + return mScratchBuffer.get(requstedSizeBytes, scratchBufferOut); +} + +bool Context::getZeroFilledBuffer(size_t requstedSizeBytes, + angle::MemoryBuffer **zeroBufferOut) const +{ + return mZeroFilledBuffer.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_CONTEXT_TRY(mImplementation->dispatchCompute(this, numGroupsX, numGroupsY, numGroupsZ)); +} + +void Context::dispatchComputeIndirect(GLintptr indirect) +{ + ANGLE_CONTEXT_TRY(prepareForDispatch()); + ANGLE_CONTEXT_TRY(mImplementation->dispatchComputeIndirect(this, indirect)); +} + +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)); + Program *programObject = mState.getLinkedProgram(this); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + if (hasDrawID) + { + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) + { + if (noopDraw(mode, counts[drawID])) + { + continue; + } + programObject->setDrawIDUniform(drawID); + ANGLE_CONTEXT_TRY( + mImplementation->drawArrays(this, mode, firsts[drawID], counts[drawID])); + MarkTransformFeedbackBufferUsage(this, counts[drawID], 1); + } + } + else + { + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) + { + if (noopDraw(mode, counts[drawID])) + { + continue; + } + ANGLE_CONTEXT_TRY( + mImplementation->drawArrays(this, mode, firsts[drawID], counts[drawID])); + MarkTransformFeedbackBufferUsage(this, counts[drawID], 1); + } + } +} + +void Context::multiDrawArraysInstanced(PrimitiveMode mode, + const GLint *firsts, + const GLsizei *counts, + const GLsizei *instanceCounts, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + Program *programObject = mState.getLinkedProgram(this); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + if (hasDrawID) + { + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) + { + if (noopDrawInstanced(mode, counts[drawID], instanceCounts[drawID])) + { + continue; + } + programObject->setDrawIDUniform(drawID); + ANGLE_CONTEXT_TRY(mImplementation->drawArraysInstanced( + this, mode, firsts[drawID], counts[drawID], instanceCounts[drawID])); + MarkTransformFeedbackBufferUsage(this, counts[drawID], instanceCounts[drawID]); + } + } + else + { + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) + { + if (noopDrawInstanced(mode, counts[drawID], instanceCounts[drawID])) + { + continue; + } + ANGLE_CONTEXT_TRY(mImplementation->drawArraysInstanced( + this, mode, firsts[drawID], counts[drawID], instanceCounts[drawID])); + MarkTransformFeedbackBufferUsage(this, counts[drawID], instanceCounts[drawID]); + } + } +} + +void Context::multiDrawElements(PrimitiveMode mode, + const GLsizei *counts, + DrawElementsType type, + const GLvoid *const *indices, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + Program *programObject = mState.getLinkedProgram(this); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + if (hasDrawID) + { + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) + { + if (noopDraw(mode, counts[drawID])) + { + continue; + } + programObject->setDrawIDUniform(drawID); + ANGLE_CONTEXT_TRY( + mImplementation->drawElements(this, mode, counts[drawID], type, indices[drawID])); + } + } + else + { + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) + { + if (noopDraw(mode, counts[drawID])) + { + continue; + } + ANGLE_CONTEXT_TRY( + mImplementation->drawElements(this, mode, counts[drawID], type, indices[drawID])); + } + } +} + +void Context::multiDrawElementsInstanced(PrimitiveMode mode, + const GLsizei *counts, + DrawElementsType type, + const GLvoid *const *indices, + const GLsizei *instanceCounts, + GLsizei drawcount) +{ + ANGLE_CONTEXT_TRY(prepareForDraw(mode)); + Program *programObject = mState.getLinkedProgram(this); + const bool hasDrawID = programObject && programObject->hasDrawIDUniform(); + if (hasDrawID) + { + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) + { + if (noopDrawInstanced(mode, counts[drawID], instanceCounts[drawID])) + { + continue; + } + programObject->setDrawIDUniform(drawID); + ANGLE_CONTEXT_TRY(mImplementation->drawElementsInstanced( + this, mode, counts[drawID], type, indices[drawID], instanceCounts[drawID])); + } + } + else + { + for (GLsizei drawID = 0; drawID < drawcount; ++drawID) + { + if (noopDrawInstanced(mode, counts[drawID], instanceCounts[drawID])) + { + continue; + } + ANGLE_CONTEXT_TRY(mImplementation->drawElementsInstanced( + this, mode, counts[drawID], type, indices[drawID], instanceCounts[drawID])); + } + } +} + +void Context::provokingVertex(ProvokingVertexConvention provokeMode) +{ + mState.setProvokingVertex(provokeMode); +} + +GLenum Context::checkFramebufferStatus(GLenum target) +{ + Framebuffer *framebuffer = mState.getTargetFramebuffer(target); + ASSERT(framebuffer); + return framebuffer->checkStatus(this); +} + +void Context::compileShader(GLuint shader) +{ + Shader *shaderObject = GetValidShader(this, shader); + if (!shaderObject) + { + return; + } + shaderObject->compile(this); +} + +void Context::deleteBuffers(GLsizei n, const GLuint *buffers) +{ + for (int i = 0; i < n; i++) + { + deleteBuffer(buffers[i]); + } +} + +void Context::deleteFramebuffers(GLsizei n, const GLuint *framebuffers) +{ + for (int i = 0; i < n; i++) + { + if (framebuffers[i] != 0) + { + deleteFramebuffer(framebuffers[i]); + } + } +} + +void Context::deleteRenderbuffers(GLsizei n, const GLuint *renderbuffers) +{ + for (int i = 0; i < n; i++) + { + deleteRenderbuffer(renderbuffers[i]); + } +} + +void Context::deleteTextures(GLsizei n, const GLuint *textures) +{ + for (int i = 0; i < n; i++) + { + if (textures[i] != 0) + { + deleteTexture(textures[i]); + } + } +} + +void Context::detachShader(GLuint program, GLuint shader) +{ + Program *programObject = getProgramNoResolveLink(program); + ASSERT(programObject); + + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + + programObject->detachShader(this, shaderObject); +} + +void Context::genBuffers(GLsizei n, GLuint *buffers) +{ + for (int i = 0; i < n; i++) + { + buffers[i] = createBuffer(); + } +} + +void Context::genFramebuffers(GLsizei n, GLuint *framebuffers) +{ + for (int i = 0; i < n; i++) + { + framebuffers[i] = createFramebuffer(); + } +} + +void Context::genRenderbuffers(GLsizei n, GLuint *renderbuffers) +{ + for (int i = 0; i < n; i++) + { + renderbuffers[i] = createRenderbuffer(); + } +} + +void Context::genTextures(GLsizei n, GLuint *textures) +{ + for (int i = 0; i < n; i++) + { + textures[i] = createTexture(); + } +} + +void Context::getActiveAttrib(GLuint 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(GLuint 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(GLuint program, GLsizei maxcount, GLsizei *count, GLuint *shaders) +{ + Program *programObject = getProgramNoResolveLink(program); + ASSERT(programObject); + programObject->getAttachedShaders(maxcount, count, shaders); +} + +GLint Context::getAttribLocation(GLuint 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; + 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(GLuint program, GLenum pname, GLint *params) +{ + if (mContextLost) + return; // This must be a no-error context, to skip context-loss validation! + + // Don't resolve link if checking the link completion status. + Program *programObject = (pname == GL_COMPLETION_STATUS_KHR ? getProgramNoResolveLink(program) + : getProgramResolveLink(program)); + ASSERT(programObject); + QueryProgramiv(this, programObject, pname, params); +} + +void Context::getProgramivRobust(GLuint program, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getProgramiv(program, pname, params); +} + +void Context::getProgramPipelineiv(GLuint pipeline, GLenum pname, GLint *params) +{ + UNIMPLEMENTED(); +} + +MemoryObject *Context::getMemoryObject(GLuint handle) const +{ + return mState.mMemoryObjectManager->getMemoryObject(handle); +} + +Semaphore *Context::getSemaphore(GLuint handle) const +{ + return mState.mSemaphoreManager->getSemaphore(handle); +} + +void Context::getProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei *length, GLchar *infolog) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getInfoLog(bufsize, length, infolog); +} + +void Context::getProgramPipelineInfoLog(GLuint pipeline, + GLsizei bufSize, + GLsizei *length, + GLchar *infoLog) +{ + UNIMPLEMENTED(); +} + +void Context::getShaderiv(GLuint shader, GLenum pname, GLint *params) +{ + Shader *shaderObject = nullptr; + if (!mContextLost) + { + shaderObject = getShader(shader); + ASSERT(shaderObject); + } + QueryShaderiv(this, shaderObject, pname, params); +} + +void Context::getShaderivRobust(GLuint shader, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getShaderiv(shader, pname, params); +} + +void Context::getShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *infolog) +{ + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + shaderObject->getInfoLog(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(GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *source) +{ + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + shaderObject->getSource(bufsize, length, source); +} + +void Context::getUniformfv(GLuint program, GLint location, GLfloat *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getUniformfv(this, location, params); +} + +void Context::getUniformfvRobust(GLuint program, + GLint location, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + getUniformfv(program, location, params); +} + +void Context::getUniformiv(GLuint program, GLint location, GLint *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->getUniformiv(this, location, params); +} + +void Context::getUniformivRobust(GLuint program, + GLint location, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getUniformiv(program, location, params); +} + +GLint Context::getUniformLocation(GLuint program, const GLchar *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + return programObject->getUniformLocation(name); +} + +GLboolean Context::isBuffer(GLuint buffer) +{ + if (buffer == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getBuffer(buffer)); +} + +GLboolean Context::isEnabled(GLenum cap) +{ + return mState.getEnableFeature(cap); +} + +GLboolean Context::isFramebuffer(GLuint framebuffer) +{ + if (framebuffer == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getFramebuffer(framebuffer)); +} + +GLboolean Context::isProgram(GLuint program) +{ + if (program == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getProgramNoResolveLink(program)); +} + +GLboolean Context::isRenderbuffer(GLuint renderbuffer) +{ + if (renderbuffer == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getRenderbuffer(renderbuffer)); +} + +GLboolean Context::isShader(GLuint shader) +{ + if (shader == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getShader(shader)); +} + +GLboolean Context::isTexture(GLuint texture) +{ + if (texture == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getTexture(texture)); +} + +void Context::linkProgram(GLuint 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 GLuint *shaders, + GLenum binaryformat, + const void *binary, + GLsizei length) +{ + // No binary shader formats are supported. + UNIMPLEMENTED(); +} + +void Context::bindFragDataLocationIndexed(GLuint program, + GLuint colorNumber, + GLuint index, + const char *name) +{ + Program *programObject = getProgramNoResolveLink(program); + programObject->bindFragmentOutputLocation(colorNumber, name); + programObject->bindFragmentOutputIndex(index, name); +} + +void Context::bindFragDataLocation(GLuint program, GLuint colorNumber, const char *name) +{ + bindFragDataLocationIndexed(program, colorNumber, 0u, name); +} + +int Context::getFragDataIndex(GLuint program, const char *name) +{ + Program *programObject = getProgramResolveLink(program); + return programObject->getFragDataIndex(name); +} + +int Context::getProgramResourceLocationIndex(GLuint program, + GLenum programInterface, + const char *name) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programInterface == GL_PROGRAM_OUTPUT); + return programObject->getFragDataIndex(name); +} + +void Context::shaderSource(GLuint 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::uniform1f(GLint location, GLfloat x) +{ + Program *program = mState.getProgram(); + program->setUniform1fv(location, 1, &x); +} + +void Context::uniform1fv(GLint location, GLsizei count, const GLfloat *v) +{ + Program *program = mState.getProgram(); + program->setUniform1fv(location, count, v); +} + +void Context::setUniform1iImpl(Program *program, GLint 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(GLint location, GLint x) +{ + setUniform1iImpl(mState.getProgram(), location, 1, &x); +} + +void Context::uniform1iv(GLint location, GLsizei count, const GLint *v) +{ + setUniform1iImpl(mState.getProgram(), location, count, v); +} + +void Context::uniform2f(GLint location, GLfloat x, GLfloat y) +{ + GLfloat xy[2] = {x, y}; + Program *program = mState.getProgram(); + program->setUniform2fv(location, 1, xy); +} + +void Context::uniform2fv(GLint location, GLsizei count, const GLfloat *v) +{ + Program *program = mState.getProgram(); + program->setUniform2fv(location, count, v); +} + +void Context::uniform2i(GLint location, GLint x, GLint y) +{ + GLint xy[2] = {x, y}; + Program *program = mState.getProgram(); + program->setUniform2iv(location, 1, xy); +} + +void Context::uniform2iv(GLint location, GLsizei count, const GLint *v) +{ + Program *program = mState.getProgram(); + program->setUniform2iv(location, count, v); +} + +void Context::uniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) +{ + GLfloat xyz[3] = {x, y, z}; + Program *program = mState.getProgram(); + program->setUniform3fv(location, 1, xyz); +} + +void Context::uniform3fv(GLint location, GLsizei count, const GLfloat *v) +{ + Program *program = mState.getProgram(); + program->setUniform3fv(location, count, v); +} + +void Context::uniform3i(GLint location, GLint x, GLint y, GLint z) +{ + GLint xyz[3] = {x, y, z}; + Program *program = mState.getProgram(); + program->setUniform3iv(location, 1, xyz); +} + +void Context::uniform3iv(GLint location, GLsizei count, const GLint *v) +{ + Program *program = mState.getProgram(); + program->setUniform3iv(location, count, v); +} + +void Context::uniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + GLfloat xyzw[4] = {x, y, z, w}; + Program *program = mState.getProgram(); + program->setUniform4fv(location, 1, xyzw); +} + +void Context::uniform4fv(GLint location, GLsizei count, const GLfloat *v) +{ + Program *program = mState.getProgram(); + program->setUniform4fv(location, count, v); +} + +void Context::uniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) +{ + GLint xyzw[4] = {x, y, z, w}; + Program *program = mState.getProgram(); + program->setUniform4iv(location, 1, xyzw); +} + +void Context::uniform4iv(GLint location, GLsizei count, const GLint *v) +{ + Program *program = mState.getProgram(); + program->setUniform4iv(location, count, v); +} + +void Context::uniformMatrix2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix2fv(location, count, transpose, value); +} + +void Context::uniformMatrix3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix3fv(location, count, transpose, value); +} + +void Context::uniformMatrix4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix4fv(location, count, transpose, value); +} + +void Context::validateProgram(GLuint program) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->validate(mState.mCaps); +} + +void Context::validateProgramPipeline(GLuint pipeline) +{ + UNIMPLEMENTED(); +} + +void Context::getProgramBinary(GLuint 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(GLuint 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(GLint location, GLuint v0) +{ + Program *program = mState.getProgram(); + program->setUniform1uiv(location, 1, &v0); +} + +void Context::uniform2ui(GLint location, GLuint v0, GLuint v1) +{ + Program *program = mState.getProgram(); + const GLuint xy[] = {v0, v1}; + program->setUniform2uiv(location, 1, xy); +} + +void Context::uniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2) +{ + Program *program = mState.getProgram(); + const GLuint xyz[] = {v0, v1, v2}; + program->setUniform3uiv(location, 1, xyz); +} + +void Context::uniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) +{ + Program *program = mState.getProgram(); + const GLuint xyzw[] = {v0, v1, v2, v3}; + program->setUniform4uiv(location, 1, xyzw); +} + +void Context::uniform1uiv(GLint location, GLsizei count, const GLuint *value) +{ + Program *program = mState.getProgram(); + program->setUniform1uiv(location, count, value); +} +void Context::uniform2uiv(GLint location, GLsizei count, const GLuint *value) +{ + Program *program = mState.getProgram(); + program->setUniform2uiv(location, count, value); +} + +void Context::uniform3uiv(GLint location, GLsizei count, const GLuint *value) +{ + Program *program = mState.getProgram(); + program->setUniform3uiv(location, count, value); +} + +void Context::uniform4uiv(GLint location, GLsizei count, const GLuint *value) +{ + Program *program = mState.getProgram(); + program->setUniform4uiv(location, count, value); +} + +void Context::genQueries(GLsizei n, GLuint *ids) +{ + for (GLsizei i = 0; i < n; i++) + { + GLuint handle = mQueryHandleAllocator.allocate(); + mQueryMap.assign(handle, nullptr); + ids[i] = handle; + } +} + +void Context::deleteQueries(GLsizei n, const GLuint *ids) +{ + for (int i = 0; i < n; i++) + { + GLuint query = ids[i]; + + Query *queryObject = nullptr; + if (mQueryMap.erase(query, &queryObject)) + { + mQueryHandleAllocator.release(query); + if (queryObject) + { + queryObject->release(this); + } + } + } +} + +GLboolean Context::isQuery(GLuint id) +{ + return ConvertToGLBoolean(getQuery(id, false, QueryType::InvalidEnum) != nullptr); +} + +void Context::uniformMatrix2x3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix2x3fv(location, count, transpose, value); +} + +void Context::uniformMatrix3x2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix3x2fv(location, count, transpose, value); +} + +void Context::uniformMatrix2x4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix2x4fv(location, count, transpose, value); +} + +void Context::uniformMatrix4x2fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix4x2fv(location, count, transpose, value); +} + +void Context::uniformMatrix3x4fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix3x4fv(location, count, transpose, value); +} + +void Context::uniformMatrix4x3fv(GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *program = mState.getProgram(); + program->setUniformMatrix4x3fv(location, count, transpose, value); +} + +void Context::deleteVertexArrays(GLsizei n, const GLuint *arrays) +{ + for (int arrayIndex = 0; arrayIndex < n; arrayIndex++) + { + GLuint vertexArray = arrays[arrayIndex]; + + if (arrays[arrayIndex] != 0) + { + VertexArray *vertexArrayObject = nullptr; + if (mVertexArrayMap.erase(vertexArray, &vertexArrayObject)) + { + if (vertexArrayObject != nullptr) + { + detachVertexArray(vertexArray); + vertexArrayObject->onDestroy(this); + } + + mVertexArrayHandleAllocator.release(vertexArray); + } + } + } +} + +void Context::genVertexArrays(GLsizei n, GLuint *arrays) +{ + for (int arrayIndex = 0; arrayIndex < n; arrayIndex++) + { + GLuint vertexArray = mVertexArrayHandleAllocator.allocate(); + mVertexArrayMap.assign(vertexArray, nullptr); + arrays[arrayIndex] = vertexArray; + } +} + +GLboolean Context::isVertexArray(GLuint array) +{ + if (array == 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(GLuint program, + GLsizei count, + const GLchar *const *varyings, + GLenum bufferMode) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setTransformFeedbackVaryings(count, varyings, bufferMode); +} + +void Context::getTransformFeedbackVarying(GLuint 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 GLuint *ids) +{ + for (int i = 0; i < n; i++) + { + GLuint transformFeedback = ids[i]; + if (transformFeedback == 0) + { + continue; + } + + TransformFeedback *transformFeedbackObject = nullptr; + if (mTransformFeedbackMap.erase(transformFeedback, &transformFeedbackObject)) + { + if (transformFeedbackObject != nullptr) + { + detachTransformFeedback(transformFeedback); + transformFeedbackObject->release(this); + } + + mTransformFeedbackHandleAllocator.release(transformFeedback); + } + } +} + +void Context::genTransformFeedbacks(GLsizei n, GLuint *ids) +{ + for (int i = 0; i < n; i++) + { + GLuint transformFeedback = mTransformFeedbackHandleAllocator.allocate(); + mTransformFeedbackMap.assign(transformFeedback, nullptr); + ids[i] = transformFeedback; + } +} + +GLboolean Context::isTransformFeedback(GLuint id) +{ + if (id == 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(GLuint program, GLint location, GLuint *params) +{ + const Program *programObject = getProgramResolveLink(program); + programObject->getUniformuiv(this, location, params); +} + +void Context::getUniformuivRobust(GLuint program, + GLint location, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + getUniformuiv(program, location, params); +} + +GLint Context::getFragDataLocation(GLuint program, const GLchar *name) +{ + const Program *programObject = getProgramResolveLink(program); + return programObject->getFragDataLocation(name); +} + +void Context::getUniformIndices(GLuint 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(GLuint 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(GLuint program, const GLchar *uniformBlockName) +{ + const Program *programObject = getProgramResolveLink(program); + return programObject->getUniformBlockIndex(uniformBlockName); +} + +void Context::getActiveUniformBlockiv(GLuint program, + GLuint uniformBlockIndex, + GLenum pname, + GLint *params) +{ + const Program *programObject = getProgramResolveLink(program); + QueryActiveUniformBlockiv(programObject, uniformBlockIndex, pname, params); +} + +void Context::getActiveUniformBlockivRobust(GLuint program, + GLuint uniformBlockIndex, + GLenum pname, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + getActiveUniformBlockiv(program, uniformBlockIndex, pname, params); +} + +void Context::getActiveUniformBlockName(GLuint program, + GLuint uniformBlockIndex, + GLsizei bufSize, + GLsizei *length, + GLchar *uniformBlockName) +{ + const Program *programObject = getProgramResolveLink(program); + programObject->getActiveUniformBlockName(uniformBlockIndex, bufSize, length, uniformBlockName); +} + +void Context::uniformBlockBinding(GLuint program, + GLuint 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) +{ + 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, GLuint *samplers) +{ + for (int i = 0; i < count; i++) + { + samplers[i] = mState.mSamplerManager->createSampler(); + } +} + +void Context::deleteSamplers(GLsizei count, const GLuint *samplers) +{ + for (int i = 0; i < count; i++) + { + GLuint sampler = samplers[i]; + + if (mState.mSamplerManager->getSampler(sampler)) + { + detachSampler(sampler); + } + + mState.mSamplerManager->deleteObject(this, sampler); + } +} + +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(GLuint program, GLint location, GLint v0) +{ + programUniform1iv(program, location, 1, &v0); +} + +void Context::programUniform2i(GLuint program, GLint location, GLint v0, GLint v1) +{ + GLint xy[2] = {v0, v1}; + programUniform2iv(program, location, 1, xy); +} + +void Context::programUniform3i(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) +{ + GLint xyz[3] = {v0, v1, v2}; + programUniform3iv(program, location, 1, xyz); +} + +void Context::programUniform4i(GLuint program, + GLint location, + GLint v0, + GLint v1, + GLint v2, + GLint v3) +{ + GLint xyzw[4] = {v0, v1, v2, v3}; + programUniform4iv(program, location, 1, xyzw); +} + +void Context::programUniform1ui(GLuint program, GLint location, GLuint v0) +{ + programUniform1uiv(program, location, 1, &v0); +} + +void Context::programUniform2ui(GLuint program, GLint location, GLuint v0, GLuint v1) +{ + GLuint xy[2] = {v0, v1}; + programUniform2uiv(program, location, 1, xy); +} + +void Context::programUniform3ui(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) +{ + GLuint xyz[3] = {v0, v1, v2}; + programUniform3uiv(program, location, 1, xyz); +} + +void Context::programUniform4ui(GLuint program, + GLint location, + GLuint v0, + GLuint v1, + GLuint v2, + GLuint v3) +{ + GLuint xyzw[4] = {v0, v1, v2, v3}; + programUniform4uiv(program, location, 1, xyzw); +} + +void Context::programUniform1f(GLuint program, GLint location, GLfloat v0) +{ + programUniform1fv(program, location, 1, &v0); +} + +void Context::programUniform2f(GLuint program, GLint location, GLfloat v0, GLfloat v1) +{ + GLfloat xy[2] = {v0, v1}; + programUniform2fv(program, location, 1, xy); +} + +void Context::programUniform3f(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) +{ + GLfloat xyz[3] = {v0, v1, v2}; + programUniform3fv(program, location, 1, xyz); +} + +void Context::programUniform4f(GLuint program, + GLint location, + GLfloat v0, + GLfloat v1, + GLfloat v2, + GLfloat v3) +{ + GLfloat xyzw[4] = {v0, v1, v2, v3}; + programUniform4fv(program, location, 1, xyzw); +} + +void Context::programUniform1iv(GLuint program, GLint location, GLsizei count, const GLint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + setUniform1iImpl(programObject, location, count, value); +} + +void Context::programUniform2iv(GLuint program, GLint location, GLsizei count, const GLint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform2iv(location, count, value); +} + +void Context::programUniform3iv(GLuint program, GLint location, GLsizei count, const GLint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform3iv(location, count, value); +} + +void Context::programUniform4iv(GLuint program, GLint location, GLsizei count, const GLint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform4iv(location, count, value); +} + +void Context::programUniform1uiv(GLuint program, GLint location, GLsizei count, const GLuint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform1uiv(location, count, value); +} + +void Context::programUniform2uiv(GLuint program, GLint location, GLsizei count, const GLuint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform2uiv(location, count, value); +} + +void Context::programUniform3uiv(GLuint program, GLint location, GLsizei count, const GLuint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform3uiv(location, count, value); +} + +void Context::programUniform4uiv(GLuint program, GLint location, GLsizei count, const GLuint *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform4uiv(location, count, value); +} + +void Context::programUniform1fv(GLuint program, GLint location, GLsizei count, const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform1fv(location, count, value); +} + +void Context::programUniform2fv(GLuint program, GLint location, GLsizei count, const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform2fv(location, count, value); +} + +void Context::programUniform3fv(GLuint program, GLint location, GLsizei count, const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform3fv(location, count, value); +} + +void Context::programUniform4fv(GLuint program, GLint location, GLsizei count, const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniform4fv(location, count, value); +} + +void Context::programUniformMatrix2fv(GLuint program, + GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix2fv(location, count, transpose, value); +} + +void Context::programUniformMatrix3fv(GLuint program, + GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix3fv(location, count, transpose, value); +} + +void Context::programUniformMatrix4fv(GLuint program, + GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix4fv(location, count, transpose, value); +} + +void Context::programUniformMatrix2x3fv(GLuint program, + GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix2x3fv(location, count, transpose, value); +} + +void Context::programUniformMatrix3x2fv(GLuint program, + GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix3x2fv(location, count, transpose, value); +} + +void Context::programUniformMatrix2x4fv(GLuint program, + GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix2x4fv(location, count, transpose, value); +} + +void Context::programUniformMatrix4x2fv(GLuint program, + GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix4x2fv(location, count, transpose, value); +} + +void Context::programUniformMatrix3x4fv(GLuint program, + GLint location, + GLsizei count, + GLboolean transpose, + const GLfloat *value) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + programObject->setUniformMatrix3x4fv(location, count, transpose, value); +} + +void Context::programUniformMatrix4x3fv(GLuint program, + GLint 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, GLuint *pipelines) +{ + for (int i = 0; i < count; i++) + { + pipelines[i] = createProgramPipeline(); + } +} + +void Context::deleteProgramPipelines(GLsizei count, const GLuint *pipelines) +{ + for (int i = 0; i < count; i++) + { + if (pipelines[i] != 0) + { + deleteProgramPipeline(pipelines[i]); + } + } +} + +GLboolean Context::isProgramPipeline(GLuint pipeline) +{ + if (pipeline == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getProgramPipeline(pipeline)); +} + +void Context::finishFenceNV(GLuint fence) +{ + FenceNV *fenceObject = getFenceNV(fence); + + ASSERT(fenceObject && fenceObject->isSet()); + ANGLE_CONTEXT_TRY(fenceObject->finish(this)); +} + +void Context::getFenceivNV(GLuint 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(GLuint shader, + GLsizei bufsize, + GLsizei *length, + GLchar *source) +{ + Shader *shaderObject = getShader(shader); + ASSERT(shaderObject); + shaderObject->getTranslatedSourceWithDebugInfo(bufsize, length, source); +} + +void Context::getnUniformfv(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + + programObject->getUniformfv(this, location, params); +} + +void Context::getnUniformfvRobust(GLuint program, + GLint location, + GLsizei bufSize, + GLsizei *length, + GLfloat *params) +{ + UNIMPLEMENTED(); +} + +void Context::getnUniformiv(GLuint program, GLint location, GLsizei bufSize, GLint *params) +{ + Program *programObject = getProgramResolveLink(program); + ASSERT(programObject); + + programObject->getUniformiv(this, location, params); +} + +void Context::getnUniformivRobust(GLuint program, + GLint location, + GLsizei bufSize, + GLsizei *length, + GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getnUniformuivRobust(GLuint program, + GLint location, + GLsizei bufSize, + GLsizei *length, + GLuint *params) +{ + UNIMPLEMENTED(); +} + +GLboolean Context::isFenceNV(GLuint fence) +{ + 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(GLuint 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(GLuint 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 GLuint *memoryObjects) +{ + for (int i = 0; i < n; i++) + { + deleteMemoryObject(memoryObjects[i]); + } +} + +GLboolean Context::isMemoryObject(GLuint memoryObject) +{ + if (memoryObject == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getMemoryObject(memoryObject)); +} + +void Context::createMemoryObjects(GLsizei n, GLuint *memoryObjects) +{ + for (int i = 0; i < n; i++) + { + memoryObjects[i] = createMemoryObject(); + } +} + +void Context::memoryObjectParameteriv(GLuint memoryObject, GLenum pname, const GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::getMemoryObjectParameteriv(GLuint memoryObject, GLenum pname, GLint *params) +{ + UNIMPLEMENTED(); +} + +void Context::texStorageMem2D(TextureType target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLuint memory, + GLuint64 offset) +{ + 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)); +} + +void Context::texStorageMem2DMultisample(TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLboolean fixedSampleLocations, + GLuint memory, + GLuint64 offset) +{ + UNIMPLEMENTED(); +} + +void Context::texStorageMem3D(TextureType target, + GLsizei levels, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLuint memory, + GLuint64 offset) +{ + UNIMPLEMENTED(); +} + +void Context::texStorageMem3DMultisample(TextureType target, + GLsizei samples, + GLenum internalFormat, + GLsizei width, + GLsizei height, + GLsizei depth, + GLboolean fixedSampleLocations, + GLuint memory, + GLuint64 offset) +{ + UNIMPLEMENTED(); +} + +void Context::bufferStorageMem(TextureType target, GLsizeiptr size, GLuint memory, GLuint64 offset) +{ + UNIMPLEMENTED(); +} + +void Context::importMemoryFd(GLuint 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::genSemaphores(GLsizei n, GLuint *semaphores) +{ + for (int i = 0; i < n; i++) + { + semaphores[i] = createSemaphore(); + } +} + +void Context::deleteSemaphores(GLsizei n, const GLuint *semaphores) +{ + for (int i = 0; i < n; i++) + { + deleteSemaphore(semaphores[i]); + } +} + +GLboolean Context::isSemaphore(GLuint semaphore) +{ + if (semaphore == 0) + { + return GL_FALSE; + } + + return ConvertToGLBoolean(getSemaphore(semaphore)); +} + +void Context::semaphoreParameterui64v(GLuint semaphore, GLenum pname, const GLuint64 *params) +{ + UNIMPLEMENTED(); +} + +void Context::getSemaphoreParameterui64v(GLuint semaphore, GLenum pname, GLuint64 *params) +{ + UNIMPLEMENTED(); +} + +void Context::waitSemaphore(GLuint semaphoreHandle, + GLuint numBufferBarriers, + const GLuint *buffers, + GLuint numTextureBarriers, + const GLuint *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(GLuint semaphoreHandle, + GLuint numBufferBarriers, + const GLuint *buffers, + GLuint numTextureBarriers, + const GLuint *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(GLuint semaphore, HandleType handleType, GLint fd) +{ + Semaphore *semaphoreObject = getSemaphore(semaphore); + ASSERT(semaphoreObject != nullptr); + ANGLE_CONTEXT_TRY(semaphoreObject->importFd(this, handleType, fd)); +} + +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::texStorage1D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) +{ + UNIMPLEMENTED(); +} + +bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *numParams) const +{ + // Please note: the query type returned for DEPTH_CLEAR_VALUE in this implementation + // is FLOAT rather than INT, as would be suggested by the GL ES 2.0 spec. This is due + // to the fact that it is stored internally as a float, and so would require conversion + // if returned from Context::getIntegerv. Since this conversion is already implemented + // in the case that one calls glGetIntegerv to retrieve a float-typed state variable, we + // place DEPTH_CLEAR_VALUE with the floats. This should make no difference to the calling + // application. + switch (pname) + { + case GL_COMPRESSED_TEXTURE_FORMATS: + { + *type = GL_INT; + *numParams = static_cast<unsigned int>(getCaps().compressedTextureFormats.size()); + return true; + } + case GL_SHADER_BINARY_FORMATS: + { + *type = GL_INT; + *numParams = static_cast<unsigned int>(getCaps().shaderBinaryFormats.size()); + return true; + } + + case GL_MAX_VERTEX_ATTRIBS: + case GL_MAX_VERTEX_UNIFORM_VECTORS: + case GL_MAX_VARYING_VECTORS: + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: + case GL_MAX_TEXTURE_IMAGE_UNITS: + case GL_MAX_FRAGMENT_UNIFORM_VECTORS: + case GL_MAX_RENDERBUFFER_SIZE: + case GL_NUM_SHADER_BINARY_FORMATS: + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: + case GL_ARRAY_BUFFER_BINDING: + case GL_FRAMEBUFFER_BINDING: // GL_FRAMEBUFFER_BINDING now equivalent to + // GL_DRAW_FRAMEBUFFER_BINDING_ANGLE + case GL_RENDERBUFFER_BINDING: + case GL_CURRENT_PROGRAM: + case GL_PACK_ALIGNMENT: + case GL_UNPACK_ALIGNMENT: + case GL_GENERATE_MIPMAP_HINT: + case GL_RED_BITS: + case GL_GREEN_BITS: + case GL_BLUE_BITS: + case GL_ALPHA_BITS: + case GL_DEPTH_BITS: + case GL_STENCIL_BITS: + case GL_ELEMENT_ARRAY_BUFFER_BINDING: + case GL_CULL_FACE_MODE: + case GL_FRONT_FACE: + case GL_ACTIVE_TEXTURE: + case GL_STENCIL_FUNC: + case GL_STENCIL_VALUE_MASK: + case GL_STENCIL_REF: + case GL_STENCIL_FAIL: + case GL_STENCIL_PASS_DEPTH_FAIL: + case GL_STENCIL_PASS_DEPTH_PASS: + case GL_STENCIL_BACK_FUNC: + case GL_STENCIL_BACK_VALUE_MASK: + case GL_STENCIL_BACK_REF: + case GL_STENCIL_BACK_FAIL: + case GL_STENCIL_BACK_PASS_DEPTH_FAIL: + case GL_STENCIL_BACK_PASS_DEPTH_PASS: + case GL_DEPTH_FUNC: + 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: + case GL_STENCIL_WRITEMASK: + case GL_STENCIL_BACK_WRITEMASK: + case GL_STENCIL_CLEAR_VALUE: + case GL_SUBPIXEL_BITS: + case GL_MAX_TEXTURE_SIZE: + case GL_MAX_CUBE_MAP_TEXTURE_SIZE: + case GL_SAMPLE_BUFFERS: + case GL_SAMPLES: + case GL_IMPLEMENTATION_COLOR_READ_TYPE: + case GL_IMPLEMENTATION_COLOR_READ_FORMAT: + case GL_TEXTURE_BINDING_2D: + case GL_TEXTURE_BINDING_CUBE_MAP: + case GL_RESET_NOTIFICATION_STRATEGY_EXT: + { + *type = GL_INT; + *numParams = 1; + return true; + } + case GL_PACK_REVERSE_ROW_ORDER_ANGLE: + { + if (!getExtensions().packReverseRowOrder) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + case GL_MAX_RECTANGLE_TEXTURE_SIZE_ANGLE: + case GL_TEXTURE_BINDING_RECTANGLE_ANGLE: + { + if (!getExtensions().textureRectangle) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + case GL_MAX_DRAW_BUFFERS_EXT: + case GL_MAX_COLOR_ATTACHMENTS_EXT: + { + if ((getClientMajorVersion() < 3) && !getExtensions().drawBuffers) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + case GL_MAX_VIEWPORT_DIMS: + { + *type = GL_INT; + *numParams = 2; + return true; + } + case GL_VIEWPORT: + case GL_SCISSOR_BOX: + { + *type = GL_INT; + *numParams = 4; + return true; + } + case GL_SHADER_COMPILER: + case GL_SAMPLE_COVERAGE_INVERT: + case GL_DEPTH_WRITEMASK: + case GL_CULL_FACE: // CULL_FACE through DITHER are natural to IsEnabled, + case GL_POLYGON_OFFSET_FILL: // but can be retrieved through the Get{Type}v queries. + case GL_SAMPLE_ALPHA_TO_COVERAGE: // For this purpose, they are treated here as + // bool-natural + case GL_SAMPLE_COVERAGE: + case GL_SCISSOR_TEST: + case GL_STENCIL_TEST: + case GL_DEPTH_TEST: + case GL_BLEND: + case GL_DITHER: + case GL_CONTEXT_ROBUST_ACCESS_EXT: + { + *type = GL_BOOL; + *numParams = 1; + return true; + } + case GL_COLOR_WRITEMASK: + { + *type = GL_BOOL; + *numParams = 4; + return true; + } + case GL_POLYGON_OFFSET_FACTOR: + case GL_POLYGON_OFFSET_UNITS: + case GL_SAMPLE_COVERAGE_VALUE: + case GL_DEPTH_CLEAR_VALUE: + case GL_LINE_WIDTH: + { + *type = GL_FLOAT; + *numParams = 1; + return true; + } + case GL_ALIASED_LINE_WIDTH_RANGE: + case GL_ALIASED_POINT_SIZE_RANGE: + case GL_DEPTH_RANGE: + { + *type = GL_FLOAT; + *numParams = 2; + return true; + } + case GL_COLOR_CLEAR_VALUE: + case GL_BLEND_COLOR: + { + *type = GL_FLOAT; + *numParams = 4; + return true; + } + case GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: + if (!getExtensions().textureFilterAnisotropic) + { + return false; + } + *type = GL_FLOAT; + *numParams = 1; + return true; + case GL_TIMESTAMP_EXT: + if (!getExtensions().disjointTimerQuery) + { + return false; + } + *type = GL_INT_64_ANGLEX; + *numParams = 1; + return true; + case GL_GPU_DISJOINT_EXT: + if (!getExtensions().disjointTimerQuery) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_COVERAGE_MODULATION_CHROMIUM: + if (!getExtensions().framebufferMixedSamples) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_TEXTURE_BINDING_EXTERNAL_OES: + if (!getExtensions().eglStreamConsumerExternal && !getExtensions().eglImageExternal) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + + if (getExtensions().debug) + { + switch (pname) + { + case GL_DEBUG_LOGGED_MESSAGES: + case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH: + case GL_DEBUG_GROUP_STACK_DEPTH: + case GL_MAX_DEBUG_MESSAGE_LENGTH: + case GL_MAX_DEBUG_LOGGED_MESSAGES: + case GL_MAX_DEBUG_GROUP_STACK_DEPTH: + case GL_MAX_LABEL_LENGTH: + *type = GL_INT; + *numParams = 1; + return true; + + case GL_DEBUG_OUTPUT_SYNCHRONOUS: + case GL_DEBUG_OUTPUT: + *type = GL_BOOL; + *numParams = 1; + return true; + } + } + + if (getExtensions().multisampleCompatibility) + { + switch (pname) + { + case GL_MULTISAMPLE_EXT: + case GL_SAMPLE_ALPHA_TO_ONE_EXT: + *type = GL_BOOL; + *numParams = 1; + return true; + } + } + + if (getExtensions().pathRendering) + { + switch (pname) + { + case GL_PATH_MODELVIEW_MATRIX_CHROMIUM: + case GL_PATH_PROJECTION_MATRIX_CHROMIUM: + *type = GL_FLOAT; + *numParams = 16; + return true; + } + } + + if (getExtensions().bindGeneratesResource) + { + switch (pname) + { + case GL_BIND_GENERATES_RESOURCE_CHROMIUM: + *type = GL_BOOL; + *numParams = 1; + return true; + } + } + + if (getExtensions().clientArrays) + { + switch (pname) + { + case GL_CLIENT_ARRAYS_ANGLE: + *type = GL_BOOL; + *numParams = 1; + return true; + } + } + + if (getExtensions().sRGBWriteControl) + { + switch (pname) + { + case GL_FRAMEBUFFER_SRGB_EXT: + *type = GL_BOOL; + *numParams = 1; + return true; + } + } + + if (getExtensions().robustResourceInitialization && + pname == GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE) + { + *type = GL_BOOL; + *numParams = 1; + return true; + } + + if (getExtensions().programCacheControl && pname == GL_PROGRAM_CACHE_ENABLED_ANGLE) + { + *type = GL_BOOL; + *numParams = 1; + return true; + } + + if (getExtensions().parallelShaderCompile && pname == GL_MAX_SHADER_COMPILER_THREADS_KHR) + { + *type = GL_INT; + *numParams = 1; + return true; + } + + if (getExtensions().blendFuncExtended && pname == GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT) + { + *type = GL_INT; + *numParams = 1; + return true; + } + + // Check for ES3.0+ parameter names which are also exposed as ES2 extensions + switch (pname) + { + // GL_DRAW_FRAMEBUFFER_BINDING_ANGLE equivalent to FRAMEBUFFER_BINDING + case GL_READ_FRAMEBUFFER_BINDING_ANGLE: + if ((getClientMajorVersion() < 3) && !getExtensions().framebufferBlit) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + + case GL_NUM_PROGRAM_BINARY_FORMATS_OES: + if ((getClientMajorVersion() < 3) && !getExtensions().getProgramBinary) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + + case GL_PROGRAM_BINARY_FORMATS_OES: + if ((getClientMajorVersion() < 3) && !getExtensions().getProgramBinary) + { + return false; + } + *type = GL_INT; + *numParams = static_cast<unsigned int>(getCaps().programBinaryFormats.size()); + return true; + + case GL_PACK_ROW_LENGTH: + case GL_PACK_SKIP_ROWS: + case GL_PACK_SKIP_PIXELS: + if ((getClientMajorVersion() < 3) && !getExtensions().packSubimage) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_UNPACK_ROW_LENGTH: + case GL_UNPACK_SKIP_ROWS: + case GL_UNPACK_SKIP_PIXELS: + if ((getClientMajorVersion() < 3) && !getExtensions().unpackSubimage) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_VERTEX_ARRAY_BINDING: + if ((getClientMajorVersion() < 3) && !getExtensions().vertexArrayObject) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_PIXEL_PACK_BUFFER_BINDING: + case GL_PIXEL_UNPACK_BUFFER_BINDING: + if ((getClientMajorVersion() < 3) && !getExtensions().pixelBufferObject) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_MAX_SAMPLES: + { + static_assert(GL_MAX_SAMPLES_ANGLE == GL_MAX_SAMPLES, + "GL_MAX_SAMPLES_ANGLE not equal to GL_MAX_SAMPLES"); + if ((getClientMajorVersion() < 3) && !getExtensions().framebufferMultisample) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + + case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: + if ((getClientMajorVersion() < 3) && !getExtensions().standardDerivatives) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + case GL_TEXTURE_BINDING_3D: + if ((getClientMajorVersion() < 3) && !getExtensions().texture3DOES) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + case GL_MAX_3D_TEXTURE_SIZE: + if ((getClientMajorVersion() < 3) && !getExtensions().texture3DOES) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + + if (pname >= GL_DRAW_BUFFER0_EXT && pname <= GL_DRAW_BUFFER15_EXT) + { + if ((getClientVersion() < Version(3, 0)) && !getExtensions().drawBuffers) + { + return false; + } + *type = GL_INT; + *numParams = 1; + return true; + } + + if ((getExtensions().multiview2 || getExtensions().multiview) && pname == GL_MAX_VIEWS_OVR) + { + *type = GL_INT; + *numParams = 1; + return true; + } + + if (getClientVersion() < Version(2, 0)) + { + switch (pname) + { + case GL_ALPHA_TEST_FUNC: + case GL_CLIENT_ACTIVE_TEXTURE: + case GL_MATRIX_MODE: + case GL_MAX_TEXTURE_UNITS: + case GL_MAX_MODELVIEW_STACK_DEPTH: + case GL_MAX_PROJECTION_STACK_DEPTH: + case GL_MAX_TEXTURE_STACK_DEPTH: + case GL_MAX_LIGHTS: + case GL_MAX_CLIP_PLANES: + case GL_VERTEX_ARRAY_STRIDE: + case GL_NORMAL_ARRAY_STRIDE: + case GL_COLOR_ARRAY_STRIDE: + case GL_TEXTURE_COORD_ARRAY_STRIDE: + case GL_VERTEX_ARRAY_SIZE: + case GL_COLOR_ARRAY_SIZE: + case GL_TEXTURE_COORD_ARRAY_SIZE: + case GL_VERTEX_ARRAY_TYPE: + case GL_NORMAL_ARRAY_TYPE: + case GL_COLOR_ARRAY_TYPE: + case GL_TEXTURE_COORD_ARRAY_TYPE: + case GL_VERTEX_ARRAY_BUFFER_BINDING: + case GL_NORMAL_ARRAY_BUFFER_BINDING: + case GL_COLOR_ARRAY_BUFFER_BINDING: + case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING: + case GL_POINT_SIZE_ARRAY_STRIDE_OES: + case GL_POINT_SIZE_ARRAY_TYPE_OES: + case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES: + case GL_SHADE_MODEL: + case GL_MODELVIEW_STACK_DEPTH: + case GL_PROJECTION_STACK_DEPTH: + case GL_TEXTURE_STACK_DEPTH: + case GL_LOGIC_OP_MODE: + case GL_BLEND_SRC: + case GL_BLEND_DST: + case GL_PERSPECTIVE_CORRECTION_HINT: + case GL_POINT_SMOOTH_HINT: + case GL_LINE_SMOOTH_HINT: + case GL_FOG_HINT: + *type = GL_INT; + *numParams = 1; + return true; + case GL_ALPHA_TEST_REF: + case GL_FOG_DENSITY: + case GL_FOG_START: + case GL_FOG_END: + case GL_FOG_MODE: + case GL_POINT_SIZE: + case GL_POINT_SIZE_MIN: + case GL_POINT_SIZE_MAX: + case GL_POINT_FADE_THRESHOLD_SIZE: + *type = GL_FLOAT; + *numParams = 1; + return true; + case GL_SMOOTH_POINT_SIZE_RANGE: + case GL_SMOOTH_LINE_WIDTH_RANGE: + *type = GL_FLOAT; + *numParams = 2; + return true; + case GL_CURRENT_COLOR: + case GL_CURRENT_TEXTURE_COORDS: + case GL_LIGHT_MODEL_AMBIENT: + case GL_FOG_COLOR: + *type = GL_FLOAT; + *numParams = 4; + return true; + case GL_CURRENT_NORMAL: + case GL_POINT_DISTANCE_ATTENUATION: + *type = GL_FLOAT; + *numParams = 3; + return true; + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: + case GL_TEXTURE_MATRIX: + *type = GL_FLOAT; + *numParams = 16; + return true; + case GL_LIGHT_MODEL_TWO_SIDE: + *type = GL_BOOL; + *numParams = 1; + return true; + } + } + + if (getClientVersion() < Version(3, 0)) + { + return false; + } + + // Check for ES3.0+ parameter names + switch (pname) + { + case GL_MAX_UNIFORM_BUFFER_BINDINGS: + case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: + case GL_UNIFORM_BUFFER_BINDING: + case GL_TRANSFORM_FEEDBACK_BINDING: + case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: + case GL_COPY_READ_BUFFER_BINDING: + case GL_COPY_WRITE_BUFFER_BINDING: + case GL_SAMPLER_BINDING: + case GL_READ_BUFFER: + case GL_TEXTURE_BINDING_3D: + case GL_TEXTURE_BINDING_2D_ARRAY: + case GL_MAX_ARRAY_TEXTURE_LAYERS: + case GL_MAX_VERTEX_UNIFORM_BLOCKS: + case GL_MAX_FRAGMENT_UNIFORM_BLOCKS: + case GL_MAX_COMBINED_UNIFORM_BLOCKS: + case GL_MAX_VERTEX_OUTPUT_COMPONENTS: + case GL_MAX_FRAGMENT_INPUT_COMPONENTS: + case GL_MAX_VARYING_COMPONENTS: + case GL_MAX_VERTEX_UNIFORM_COMPONENTS: + case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: + case GL_MIN_PROGRAM_TEXEL_OFFSET: + case GL_MAX_PROGRAM_TEXEL_OFFSET: + case GL_NUM_EXTENSIONS: + case GL_MAJOR_VERSION: + case GL_MINOR_VERSION: + case GL_MAX_ELEMENTS_INDICES: + case GL_MAX_ELEMENTS_VERTICES: + case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: + case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: + case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: + case GL_UNPACK_IMAGE_HEIGHT: + case GL_UNPACK_SKIP_IMAGES: + { + *type = GL_INT; + *numParams = 1; + return true; + } + + case GL_MAX_ELEMENT_INDEX: + case GL_MAX_UNIFORM_BLOCK_SIZE: + case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: + case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: + case GL_MAX_SERVER_WAIT_TIMEOUT: + { + *type = GL_INT_64_ANGLEX; + *numParams = 1; + return true; + } + + case GL_TRANSFORM_FEEDBACK_ACTIVE: + case GL_TRANSFORM_FEEDBACK_PAUSED: + case GL_PRIMITIVE_RESTART_FIXED_INDEX: + case GL_RASTERIZER_DISCARD: + { + *type = GL_BOOL; + *numParams = 1; + return true; + } + + case GL_MAX_TEXTURE_LOD_BIAS: + { + *type = GL_FLOAT; + *numParams = 1; + return true; + } + } + + if (getExtensions().requestExtension) + { + switch (pname) + { + case GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE: + *type = GL_INT; + *numParams = 1; + return true; + } + } + + if (getExtensions().textureMultisample) + { + switch (pname) + { + case GL_MAX_COLOR_TEXTURE_SAMPLES_ANGLE: + case GL_MAX_INTEGER_SAMPLES_ANGLE: + case GL_MAX_DEPTH_TEXTURE_SAMPLES_ANGLE: + case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ANGLE: + case GL_MAX_SAMPLE_MASK_WORDS: + *type = GL_INT; + *numParams = 1; + return true; + } + } + + if (getClientVersion() < Version(3, 1)) + { + return false; + } + + switch (pname) + { + case GL_ATOMIC_COUNTER_BUFFER_BINDING: + case GL_DRAW_INDIRECT_BUFFER_BINDING: + case GL_DISPATCH_INDIRECT_BUFFER_BINDING: + case GL_MAX_FRAMEBUFFER_WIDTH: + case GL_MAX_FRAMEBUFFER_HEIGHT: + case GL_MAX_FRAMEBUFFER_SAMPLES: + case GL_MAX_SAMPLE_MASK_WORDS: + case GL_MAX_COLOR_TEXTURE_SAMPLES: + case GL_MAX_DEPTH_TEXTURE_SAMPLES: + case GL_MAX_INTEGER_SAMPLES: + case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET: + case GL_MAX_VERTEX_ATTRIB_BINDINGS: + case GL_MAX_VERTEX_ATTRIB_STRIDE: + case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_VERTEX_ATOMIC_COUNTERS: + case GL_MAX_VERTEX_IMAGE_UNIFORMS: + case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: + case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_FRAGMENT_ATOMIC_COUNTERS: + case GL_MAX_FRAGMENT_IMAGE_UNIFORMS: + case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS: + case GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET: + case GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET: + case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: + case GL_MAX_COMPUTE_UNIFORM_BLOCKS: + case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS: + case GL_MAX_COMPUTE_SHARED_MEMORY_SIZE: + case GL_MAX_COMPUTE_UNIFORM_COMPONENTS: + case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_COMPUTE_ATOMIC_COUNTERS: + case GL_MAX_COMPUTE_IMAGE_UNIFORMS: + case GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS: + case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: + case GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES: + case GL_MAX_UNIFORM_LOCATIONS: + case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS: + case GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE: + case GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS: + case GL_MAX_COMBINED_ATOMIC_COUNTERS: + case GL_MAX_IMAGE_UNITS: + case GL_MAX_COMBINED_IMAGE_UNIFORMS: + case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: + case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: + case GL_SHADER_STORAGE_BUFFER_BINDING: + case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT: + case GL_TEXTURE_BINDING_2D_MULTISAMPLE: + case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY: + *type = GL_INT; + *numParams = 1; + return true; + case GL_MAX_SHADER_STORAGE_BLOCK_SIZE: + *type = GL_INT_64_ANGLEX; + *numParams = 1; + return true; + case GL_SAMPLE_MASK: + *type = GL_BOOL; + *numParams = 1; + return true; + } + + if (getExtensions().geometryShader) + { + switch (pname) + { + case GL_MAX_FRAMEBUFFER_LAYERS_EXT: + case GL_LAYER_PROVOKING_VERTEX_EXT: + case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT: + case GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT: + case GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT: + case GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT: + case GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT: + case GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT: + case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT: + case GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT: + case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT: + case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT: + case GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT: + case GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT: + case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT: + *type = GL_INT; + *numParams = 1; + return true; + } + } + + return false; +} + +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 (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(GLuint handle) const +{ + return mState.mShaderProgramManager->getProgram(handle); +} + +Shader *Context::getShader(GLuint handle) const +{ + return mState.mShaderProgramManager->getShader(handle); +} + +const angle::FrontendFeatures &Context::getFrontendFeatures() const +{ + return mDisplay->getFrontendFeatures(); +} + +bool Context::isRenderbufferGenerated(GLuint renderbuffer) const +{ + return mState.mRenderbufferManager->isHandleGenerated(renderbuffer); +} + +bool Context::isFramebufferGenerated(GLuint framebuffer) const +{ + return mState.mFramebufferManager->isHandleGenerated(framebuffer); +} + +bool Context::isProgramPipelineGenerated(GLuint pipeline) const +{ + return mState.mProgramPipelineManager->isHandleGenerated(pipeline); +} + +bool Context::usingDisplayTextureShareGroup() const +{ + return mDisplayTextureShareGroup; +} + +GLenum Context::getConvertedRenderbufferFormat(GLenum internalformat) const +{ + return mState.mExtensions.webglCompatibility && mState.mClientVersion.major == 2 && + internalformat == GL_DEPTH_STENCIL + ? GL_DEPTH24_STENCIL8 + : 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)) + { + mThreadPool = angle::WorkerThreadPool::Create(count > 0); + } + mThreadPool->setMaxThreads(count); + mImplementation->setMaxShaderCompilerThreads(count); +} + +bool Context::isGLES1() const +{ + return mState.getClientVersion() < Version(2, 0); +} + +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: + ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged); + mState.setReadFramebufferDirty(); + break; + + case kDrawFramebufferSubjectIndex: + ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged); + mState.setDrawFramebufferDirty(); + mStateCache.onDrawFramebufferChange(this); + break; + + default: + if (index < kTextureMaxSubjectIndex) + { + if (message != angle::SubjectMessage::ContentsChanged) + { + mState.onActiveTextureStateChange(this, index); + mStateCache.onActiveTextureChange(this); + } + } + else if (index < kUniformBufferMaxSubjectIndex) + { + mState.onUniformBufferStateChange(index - kUniformBuffer0SubjectIndex); + mStateCache.onUniformBufferStateChange(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)); + } + mStateCache.onProgramExecutableChange(this); + } + + return angle::Result::Continue; +} + +egl::Error Context::setDefaultFramebuffer(egl::Surface *surface) +{ + ASSERT(mCurrentSurface == nullptr); + + Framebuffer *newDefault = nullptr; + if (surface != nullptr) + { + ANGLE_TRY(surface->makeCurrent(this)); + mCurrentSurface = surface; + newDefault = surface->createDefaultFramebuffer(this); + } + else + { + newDefault = new Framebuffer(mImplementation.get()); + } + + // Update default framebuffer, the binding of the previous default + // framebuffer (or lack of) will have a nullptr. + { + mState.mFramebufferManager->setDefaultFramebuffer(newDefault); + if (mState.getReadFramebuffer() == nullptr) + { + bindReadFramebuffer(0); + } + if (mState.getDrawFramebuffer() == nullptr) + { + bindDrawFramebuffer(0); + } + } + + return egl::NoError(); +} + +egl::Error Context::unsetDefaultFramebuffer() +{ + gl::Framebuffer *defaultFramebuffer = mState.mFramebufferManager->getFramebuffer(0); + + // Remove the default framebuffer + if (mState.getReadFramebuffer() == defaultFramebuffer) + { + mState.setReadFramebufferBinding(nullptr); + mReadFramebufferObserverBinding.bind(nullptr); + } + + if (mState.getDrawFramebuffer() == defaultFramebuffer) + { + mState.setDrawFramebufferBinding(nullptr); + mDrawFramebufferObserverBinding.bind(nullptr); + } + + if (defaultFramebuffer) + { + defaultFramebuffer->onDestroy(this); + delete defaultFramebuffer; + } + + mState.mFramebufferManager->setDefaultFramebuffer(nullptr); + + // Always unset the current surface, even if setIsCurrent fails. + egl::Surface *surface = mCurrentSurface; + mCurrentSurface = nullptr; + if (surface) + { + ANGLE_TRY(surface->unMakeCurrent(this)); + } + + return egl::NoError(); +} + +angle::FrameCapture *Context::getFrameCapture() +{ + return mDisplay->getFrameCapture(); +} + +// 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, message, + gl::LOG_WARN); +} + +void ErrorSet::validationError(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); +} + +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), + mCachedTransformFeedbackActiveUnpaused(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); +} + +void StateCache::updateActiveAttribsMask(Context *context) +{ + bool isGLES1 = context->isGLES1(); + const State &glState = context->getState(); + + if (!isGLES1 && !glState.getProgram()) + { + mCachedActiveBufferedAttribsMask = AttributesMask(); + mCachedActiveClientAttribsMask = AttributesMask(); + mCachedActiveDefaultAttribsMask = AttributesMask(); + return; + } + + AttributesMask activeAttribs = isGLES1 ? glState.gles1().getVertexArraysAttributeMask() + : glState.getProgram()->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().getProgram()->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::updateBasicDrawElementsError() +{ + mCachedBasicDrawElementsError = kInvalidPointer; +} + +intptr_t StateCache::getBasicDrawStatesErrorImpl(Context *context) const +{ + ASSERT(mCachedBasicDrawStatesError == kInvalidPointer); + mCachedBasicDrawStatesError = reinterpret_cast<intptr_t>(ValidateDrawStates(context)); + return mCachedBasicDrawStatesError; +} + +intptr_t StateCache::getBasicDrawElementsErrorImpl(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(); +} + +void StateCache::onProgramExecutableChange(Context *context) +{ + updateActiveAttribsMask(context); + updateVertexElementLimits(context); + updateBasicDrawStatesError(); + updateValidDrawModes(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(); +} + +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::onColorMaskChange(Context *context) +{ + updateBasicDrawStatesError(); +} + +void StateCache::setValidDrawModes(bool pointsOK, + bool linesOK, + bool trisOK, + bool lineAdjOK, + bool triAdjOK) +{ + mCachedValidDrawModes[PrimitiveMode::Points] = pointsOK; + mCachedValidDrawModes[PrimitiveMode::Lines] = linesOK; + mCachedValidDrawModes[PrimitiveMode::LineStrip] = linesOK; + mCachedValidDrawModes[PrimitiveMode::LineLoop] = linesOK; + mCachedValidDrawModes[PrimitiveMode::Triangles] = trisOK; + mCachedValidDrawModes[PrimitiveMode::TriangleFan] = trisOK; + mCachedValidDrawModes[PrimitiveMode::TriangleStrip] = trisOK; + mCachedValidDrawModes[PrimitiveMode::LinesAdjacency] = lineAdjOK; + mCachedValidDrawModes[PrimitiveMode::LineStripAdjacency] = lineAdjOK; + mCachedValidDrawModes[PrimitiveMode::TrianglesAdjacency] = triAdjOK; + mCachedValidDrawModes[PrimitiveMode::TriangleStripAdjacency] = triAdjOK; +} + +void StateCache::updateValidDrawModes(Context *context) +{ + const State &state = context->getState(); + Program *program = state.getProgram(); + + 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().geometryShader) + { + mCachedValidDrawModes.fill(false); + mCachedValidDrawModes[curTransformFeedback->getPrimitiveMode()] = true; + return; + } + + // EXT_geometry_shader validation text: + // When transform feedback is active and not paused, all geometric primitives generated must + // be compatible with the value of <primitiveMode> passed to BeginTransformFeedback. If a + // geometry shader is active, the type of primitive emitted by that shader is used instead + // of the <mode> parameter passed to drawing commands for the purposes of this error check. + // Any primitive type may be used while transform feedback is paused. + bool pointsOK = curTransformFeedback->getPrimitiveMode() == PrimitiveMode::Points; + bool linesOK = curTransformFeedback->getPrimitiveMode() == PrimitiveMode::Lines; + bool trisOK = curTransformFeedback->getPrimitiveMode() == PrimitiveMode::Triangles; + + setValidDrawModes(pointsOK, linesOK, trisOK, false, false); + return; + } + + if (!program || !program->hasLinkedShaderStage(ShaderType::Geometry)) + { + mCachedValidDrawModes = kValidBasicDrawModes; + return; + } + + ASSERT(program && program->hasLinkedShaderStage(ShaderType::Geometry)); + PrimitiveMode gsMode = program->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); +} + +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.textureMultisample}, + {TextureType::_2DMultisampleArray, exts.textureStorageMultisample2DArray}, + {TextureType::_3D, isGLES3 || exts.texture3DOES}, + {TextureType::External, exts.eglImageExternal || exts.eglStreamConsumerExternal}, + {TextureType::Rectangle, exts.textureRectangle}, + {TextureType::CubeMap, true}, + }}; +} + +void StateCache::updateValidDrawElementsTypes(Context *context) +{ + bool supportsUint = + (context->getClientMajorVersion() >= 3 || context->getExtensions().elementIndexUint); + + 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) +{ + 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}, + }}; + } + 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::UnsignedInt2101010, VertexAttribTypeCase::ValidSize4Only}, + }}; + + 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}, + }}; + } +} +} // namespace gl |