// // Copyright 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. // // State.cpp: Implements the State class, encapsulating raw GL state. #include "libANGLE/State.h" #include #include #include "common/bitset_utils.h" #include "common/mathutil.h" #include "common/matrix_utils.h" #include "libANGLE/Buffer.h" #include "libANGLE/Caps.h" #include "libANGLE/Context.h" #include "libANGLE/Debug.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/PixelLocalStorage.h" #include "libANGLE/Query.h" #include "libANGLE/VertexArray.h" #include "libANGLE/formatutils.h" #include "libANGLE/queryconversions.h" #include "libANGLE/queryutils.h" #include "libANGLE/renderer/ContextImpl.h" #include "libANGLE/renderer/TextureImpl.h" namespace gl { namespace { bool GetAlternativeQueryType(QueryType type, QueryType *alternativeType) { switch (type) { case QueryType::AnySamples: *alternativeType = QueryType::AnySamplesConservative; return true; case QueryType::AnySamplesConservative: *alternativeType = QueryType::AnySamples; return true; default: return false; } } // Mapping from a buffer binding type to a dirty bit type. constexpr angle::PackedEnumMap kBufferBindingDirtyBits = {{ {BufferBinding::AtomicCounter, State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING}, {BufferBinding::DispatchIndirect, State::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING}, {BufferBinding::DrawIndirect, State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING}, {BufferBinding::PixelPack, State::DIRTY_BIT_PACK_BUFFER_BINDING}, {BufferBinding::PixelUnpack, State::DIRTY_BIT_UNPACK_BUFFER_BINDING}, {BufferBinding::ShaderStorage, State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING}, {BufferBinding::Uniform, State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS}, }}; // Returns a buffer binding function depending on if a dirty bit is set. template constexpr std::pair GetBufferBindingSetter() { return std::make_pair(Target, kBufferBindingDirtyBits[Target] != 0 ? &State::setGenericBufferBindingWithBit : &State::setGenericBufferBinding); } template using ContextStateMember = T *(State::*); template T *AllocateOrGetSharedResourceManager(const State *shareContextState, ContextStateMember member, T *shareResources = nullptr) { if (shareContextState) { T *resourceManager = (*shareContextState).*member; ASSERT(!resourceManager || resourceManager == shareResources || !shareResources); resourceManager->addRef(); return resourceManager; } else if (shareResources) { shareResources->addRef(); return shareResources; } else { return new T(); } } // TODO(https://anglebug.com/3889): Remove this helper function after blink and chromium part // refactory done. bool IsTextureCompatibleWithSampler(TextureType texture, TextureType sampler) { if (sampler == texture) { return true; } if (sampler == TextureType::VideoImage) { if (texture == TextureType::VideoImage || texture == TextureType::_2D) { return true; } } return false; } uint32_t gIDCounter = 1; } // namespace template ANGLE_INLINE void UpdateNonTFBufferBindingWebGL(const Context *context, BindingT *binding, Buffer *buffer, ArgsT... args) { Buffer *oldBuffer = binding->get(); if (oldBuffer) { oldBuffer->onNonTFBindingChanged(-1); oldBuffer->release(context); } binding->assign(buffer, args...); if (buffer) { buffer->addRef(); buffer->onNonTFBindingChanged(1); } } template void UpdateTFBufferBindingWebGL(const Context *context, BindingT *binding, bool indexed, ArgsT... args) { if (binding->get()) (*binding)->onTFBindingChanged(context, false, indexed); binding->set(context, args...); if (binding->get()) (*binding)->onTFBindingChanged(context, true, indexed); } void UpdateBufferBinding(const Context *context, BindingPointer *binding, Buffer *buffer, BufferBinding target) { if (context->isWebGL()) { if (target == BufferBinding::TransformFeedback) { UpdateTFBufferBindingWebGL(context, binding, false, buffer); } else { UpdateNonTFBufferBindingWebGL(context, binding, buffer); } } else { binding->set(context, buffer); } } void UpdateIndexedBufferBinding(const Context *context, OffsetBindingPointer *binding, Buffer *buffer, BufferBinding target, GLintptr offset, GLsizeiptr size) { if (context->isWebGL()) { if (target == BufferBinding::TransformFeedback) { UpdateTFBufferBindingWebGL(context, binding, true, buffer, offset, size); } else { UpdateNonTFBufferBindingWebGL(context, binding, buffer, offset, size); } } else { binding->set(context, buffer, offset, size); } } // These template functions must be defined before they are instantiated in kBufferSetters. template void State::setGenericBufferBindingWithBit(const Context *context, Buffer *buffer) { if (context->isWebGL()) { UpdateNonTFBufferBindingWebGL(context, &mBoundBuffers[Target], buffer); } else { mBoundBuffers[Target].set(context, buffer); } mDirtyBits.set(kBufferBindingDirtyBits[Target]); } template void State::setGenericBufferBinding(const Context *context, Buffer *buffer) { if (context->isWebGL()) { UpdateNonTFBufferBindingWebGL(context, &mBoundBuffers[Target], buffer); } else { mBoundBuffers[Target].set(context, buffer); } } template <> void State::setGenericBufferBinding(const Context *context, Buffer *buffer) { if (context->isWebGL()) { UpdateTFBufferBindingWebGL(context, &mBoundBuffers[BufferBinding::TransformFeedback], false, buffer); } else { mBoundBuffers[BufferBinding::TransformFeedback].set(context, buffer); } } template <> void State::setGenericBufferBinding(const Context *context, Buffer *buffer) { Buffer *oldBuffer = mVertexArray->mState.mElementArrayBuffer.get(); if (oldBuffer) { oldBuffer->removeObserver(&mVertexArray->mState.mElementArrayBuffer); oldBuffer->removeContentsObserver(mVertexArray, kElementArrayBufferIndex); if (context->isWebGL()) { oldBuffer->onNonTFBindingChanged(-1); } oldBuffer->release(context); } mVertexArray->mState.mElementArrayBuffer.assign(buffer); if (buffer) { buffer->addObserver(&mVertexArray->mState.mElementArrayBuffer); buffer->addContentsObserver(mVertexArray, kElementArrayBufferIndex); if (context->isWebGL()) { buffer->onNonTFBindingChanged(1); } buffer->addRef(); } mVertexArray->mDirtyBits.set(VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER); mVertexArray->mIndexRangeCache.invalidate(); mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } const angle::PackedEnumMap State::kBufferSetters = {{ GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), GetBufferBindingSetter(), }}; ActiveTexturesCache::ActiveTexturesCache() : mTextures{} {} ActiveTexturesCache::~ActiveTexturesCache() { ASSERT(empty()); } void ActiveTexturesCache::clear() { for (size_t textureIndex = 0; textureIndex < mTextures.size(); ++textureIndex) { reset(textureIndex); } } bool ActiveTexturesCache::empty() const { for (Texture *texture : mTextures) { if (texture) { return false; } } return true; } ANGLE_INLINE void ActiveTexturesCache::reset(size_t textureIndex) { if (mTextures[textureIndex]) { mTextures[textureIndex] = nullptr; } } ANGLE_INLINE void ActiveTexturesCache::set(size_t textureIndex, Texture *texture) { ASSERT(texture); mTextures[textureIndex] = texture; } State::State(const State *shareContextState, egl::ShareGroup *shareGroup, TextureManager *shareTextures, SemaphoreManager *shareSemaphores, const OverlayType *overlay, const EGLenum clientType, const Version &clientVersion, EGLint profileMask, bool debug, bool bindGeneratesResourceCHROMIUM, bool clientArraysEnabled, bool robustResourceInit, bool programBinaryCacheEnabled, EGLenum contextPriority, bool hasRobustAccess, bool hasProtectedContent) : mID({gIDCounter++}), mClientType(clientType), mProfileMask(profileMask), mContextPriority(contextPriority), mHasRobustAccess(hasRobustAccess), mHasProtectedContent(hasProtectedContent), mIsDebugContext(debug), mClientVersion(clientVersion), mShareGroup(shareGroup), mBufferManager(AllocateOrGetSharedResourceManager(shareContextState, &State::mBufferManager)), mShaderProgramManager( AllocateOrGetSharedResourceManager(shareContextState, &State::mShaderProgramManager)), mTextureManager(AllocateOrGetSharedResourceManager(shareContextState, &State::mTextureManager, shareTextures)), mRenderbufferManager( AllocateOrGetSharedResourceManager(shareContextState, &State::mRenderbufferManager)), mSamplerManager( AllocateOrGetSharedResourceManager(shareContextState, &State::mSamplerManager)), mSyncManager(AllocateOrGetSharedResourceManager(shareContextState, &State::mSyncManager)), mFramebufferManager(new FramebufferManager()), mProgramPipelineManager(new ProgramPipelineManager()), mMemoryObjectManager( AllocateOrGetSharedResourceManager(shareContextState, &State::mMemoryObjectManager)), mSemaphoreManager(AllocateOrGetSharedResourceManager(shareContextState, &State::mSemaphoreManager, shareSemaphores)), mDepthClearValue(0), mStencilClearValue(0), mScissorTest(false), mSampleAlphaToCoverage(false), mSampleCoverage(false), mSampleCoverageValue(0), mSampleCoverageInvert(false), mSampleMask(false), mMaxSampleMaskWords(0), mIsSampleShadingEnabled(false), mMinSampleShading(0.0f), mStencilRef(0), mStencilBackRef(0), mLineWidth(0), mGenerateMipmapHint(GL_NONE), mTextureFilteringHint(GL_NONE), mFragmentShaderDerivativeHint(GL_NONE), mBindGeneratesResource(bindGeneratesResourceCHROMIUM), mClientArraysEnabled(clientArraysEnabled), mNearZ(0), mFarZ(0), mReadFramebuffer(nullptr), mDrawFramebuffer(nullptr), mProgram(nullptr), mExecutable(nullptr), mProvokingVertex(gl::ProvokingVertexConvention::LastVertexConvention), mVertexArray(nullptr), mActiveSampler(0), mPrimitiveRestart(false), mDebug(debug), mMultiSampling(false), mSampleAlphaToOne(false), mFramebufferSRGB(true), mRobustResourceInit(robustResourceInit), mProgramBinaryCacheEnabled(programBinaryCacheEnabled), mTextureRectangleEnabled(true), mLogicOpEnabled(false), mLogicOp(LogicalOperation::Copy), mMaxShaderCompilerThreads(std::numeric_limits::max()), mPatchVertices(3), mPixelLocalStorageActive(false), mOverlay(overlay), mNoSimultaneousConstantColorAndAlphaBlendFunc(false), mSetBlendIndexedInvoked(false), mSetBlendFactorsIndexedInvoked(false), mSetBlendEquationsIndexedInvoked(false), mDisplayTextureShareGroup(shareTextures != nullptr), mBoundingBoxMinX(-1.0f), mBoundingBoxMinY(-1.0f), mBoundingBoxMinZ(-1.0f), mBoundingBoxMinW(1.0f), mBoundingBoxMaxX(1.0f), mBoundingBoxMaxY(1.0f), mBoundingBoxMaxZ(1.0f), mBoundingBoxMaxW(1.0f), mShadingRatePreserveAspectRatio(false), mShadingRate(ShadingRate::Undefined) {} State::~State() {} void State::initialize(Context *context) { const Extensions &nativeExtensions = context->getImplementation()->getNativeExtensions(); const Version &clientVersion = context->getClientVersion(); mBlendStateExt = BlendStateExt(mCaps.maxDrawBuffers); setColorClearValue(0.0f, 0.0f, 0.0f, 0.0f); mDepthClearValue = 1.0f; mStencilClearValue = 0; mScissorTest = false; mScissor.x = 0; mScissor.y = 0; mScissor.width = 0; mScissor.height = 0; mBlendColor.red = 0; mBlendColor.green = 0; mBlendColor.blue = 0; mBlendColor.alpha = 0; mStencilRef = 0; mStencilBackRef = 0; mSampleCoverage = false; mSampleCoverageValue = 1.0f; mSampleCoverageInvert = false; mMaxSampleMaskWords = static_cast(mCaps.maxSampleMaskWords); mSampleMask = false; mSampleMaskValues.fill(~GLbitfield(0)); mGenerateMipmapHint = GL_DONT_CARE; mTextureFilteringHint = GL_DONT_CARE; mFragmentShaderDerivativeHint = GL_DONT_CARE; mLineWidth = 1.0f; mViewport.x = 0; mViewport.y = 0; mViewport.width = 0; mViewport.height = 0; mNearZ = 0.0f; mFarZ = 1.0f; mClipControlOrigin = GL_LOWER_LEFT_EXT; mClipControlDepth = GL_NEGATIVE_ONE_TO_ONE_EXT; mActiveSampler = 0; mVertexAttribCurrentValues.resize(mCaps.maxVertexAttributes); // Set all indexes in state attributes type mask to float (default) for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++) { SetComponentTypeMask(ComponentType::Float, i, &mCurrentValuesTypeMask); } mUniformBuffers.resize(mCaps.maxUniformBufferBindings); mSamplerTextures[TextureType::_2D].resize(mCaps.maxCombinedTextureImageUnits); mSamplerTextures[TextureType::CubeMap].resize(mCaps.maxCombinedTextureImageUnits); if (clientVersion >= Version(3, 0) || nativeExtensions.texture3DOES) { mSamplerTextures[TextureType::_3D].resize(mCaps.maxCombinedTextureImageUnits); } if (clientVersion >= Version(3, 0)) { mSamplerTextures[TextureType::_2DArray].resize(mCaps.maxCombinedTextureImageUnits); } if (clientVersion >= Version(3, 1) || nativeExtensions.textureMultisampleANGLE) { mSamplerTextures[TextureType::_2DMultisample].resize(mCaps.maxCombinedTextureImageUnits); } if (clientVersion >= Version(3, 1)) { mSamplerTextures[TextureType::_2DMultisampleArray].resize( mCaps.maxCombinedTextureImageUnits); mAtomicCounterBuffers.resize(mCaps.maxAtomicCounterBufferBindings); mShaderStorageBuffers.resize(mCaps.maxShaderStorageBufferBindings); } if (clientVersion >= Version(3, 1) || (mExtensions.shaderPixelLocalStorageANGLE && ShPixelLocalStorageTypeUsesImages( context->getImplementation()->getNativePixelLocalStorageType()))) { mImageUnits.resize(mCaps.maxImageUnits); } if (clientVersion >= Version(3, 2) || mExtensions.textureCubeMapArrayAny()) { mSamplerTextures[TextureType::CubeMapArray].resize(mCaps.maxCombinedTextureImageUnits); } if (clientVersion >= Version(3, 2) || mExtensions.textureBufferAny()) { mSamplerTextures[TextureType::Buffer].resize(mCaps.maxCombinedTextureImageUnits); } if (nativeExtensions.textureRectangleANGLE) { mSamplerTextures[TextureType::Rectangle].resize(mCaps.maxCombinedTextureImageUnits); } if (nativeExtensions.EGLImageExternalOES || nativeExtensions.EGLStreamConsumerExternalNV) { mSamplerTextures[TextureType::External].resize(mCaps.maxCombinedTextureImageUnits); } if (nativeExtensions.videoTextureWEBGL) { mSamplerTextures[TextureType::VideoImage].resize(mCaps.maxCombinedTextureImageUnits); } mCompleteTextureBindings.reserve(mCaps.maxCombinedTextureImageUnits); for (int32_t textureIndex = 0; textureIndex < mCaps.maxCombinedTextureImageUnits; ++textureIndex) { mCompleteTextureBindings.emplace_back(context, textureIndex); } mSamplers.resize(mCaps.maxCombinedTextureImageUnits); for (QueryType type : angle::AllEnums()) { mActiveQueries[type].set(context, nullptr); } mProgram = nullptr; mExecutable = nullptr; mReadFramebuffer = nullptr; mDrawFramebuffer = nullptr; mPrimitiveRestart = false; mDebug.setMaxLoggedMessages(mCaps.maxDebugLoggedMessages); mMultiSampling = true; mSampleAlphaToOne = false; mCoverageModulation = GL_NONE; mNoSimultaneousConstantColorAndAlphaBlendFunc = context->getLimitations().noSimultaneousConstantColorAndAlphaBlendFunc || context->getExtensions().webglCompatibilityANGLE; mNoUnclampedBlendColor = context->getLimitations().noUnclampedBlendColor; // GLES1 emulation: Initialize state for GLES1 if version applies // TODO(http://anglebug.com/3745): When on desktop client only do this in compatibility profile if (clientVersion < Version(2, 0) || mClientType == EGL_OPENGL_API) { mGLES1State.initialize(context, this); } } void State::reset(const Context *context) { // Force a sync so clear doesn't end up dereferencing stale pointers. (void)syncActiveTextures(context, Command::Other); mActiveTexturesCache.clear(); for (TextureBindingVector &bindingVec : mSamplerTextures) { for (BindingPointer &texBinding : bindingVec) { texBinding.set(context, nullptr); } } for (size_t samplerIdx = 0; samplerIdx < mSamplers.size(); samplerIdx++) { mSamplers[samplerIdx].set(context, nullptr); } for (ImageUnit &imageUnit : mImageUnits) { imageUnit.texture.set(context, nullptr); imageUnit.level = 0; imageUnit.layered = false; imageUnit.layer = 0; imageUnit.access = GL_READ_ONLY; imageUnit.format = GL_R32UI; } mRenderbuffer.set(context, nullptr); for (BufferBinding type : angle::AllEnums()) { UpdateBufferBinding(context, &mBoundBuffers[type], nullptr, type); } if (mProgram) { mProgram->release(context); } mProgram = nullptr; mProgramPipeline.set(context, nullptr); mExecutable = nullptr; if (mTransformFeedback.get()) { mTransformFeedback->onBindingChanged(context, false); } mTransformFeedback.set(context, nullptr); for (QueryType type : angle::AllEnums()) { mActiveQueries[type].set(context, nullptr); } for (OffsetBindingPointer &buf : mUniformBuffers) { UpdateIndexedBufferBinding(context, &buf, nullptr, BufferBinding::Uniform, 0, 0); } mBoundUniformBuffersMask.reset(); for (OffsetBindingPointer &buf : mAtomicCounterBuffers) { UpdateIndexedBufferBinding(context, &buf, nullptr, BufferBinding::AtomicCounter, 0, 0); } mBoundAtomicCounterBuffersMask.reset(); for (OffsetBindingPointer &buf : mShaderStorageBuffers) { UpdateIndexedBufferBinding(context, &buf, nullptr, BufferBinding::ShaderStorage, 0, 0); } mBoundShaderStorageBuffersMask.reset(); mClipDistancesEnabled.reset(); setAllDirtyBits(); } ANGLE_INLINE void State::unsetActiveTextures(const ActiveTextureMask &textureMask) { // Unset any relevant bound textures. for (size_t textureIndex : textureMask) { mActiveTexturesCache.reset(textureIndex); mCompleteTextureBindings[textureIndex].reset(); } } ANGLE_INLINE void State::updateActiveTextureStateOnSync(const Context *context, size_t textureIndex, const Sampler *sampler, Texture *texture) { if (!texture || !texture->isSamplerComplete(context, sampler)) { mActiveTexturesCache.reset(textureIndex); } else { mActiveTexturesCache.set(textureIndex, texture); } mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); } ANGLE_INLINE void State::setActiveTextureDirty(size_t textureIndex, Texture *texture) { mDirtyObjects.set(DIRTY_OBJECT_ACTIVE_TEXTURES); mDirtyActiveTextures.set(textureIndex); if (!texture) { return; } if (texture->hasAnyDirtyBit()) { setTextureDirty(textureIndex); } if (mRobustResourceInit && texture->initState() == InitState::MayNeedInit) { mDirtyObjects.set(DIRTY_OBJECT_TEXTURES_INIT); } // This cache is updated immediately because we use the cache in the validation layer. // If we defer the update until syncState it's too late and we've already passed validation. if (texture && mExecutable) { // It is invalid to try to sample a non-yuv texture with a yuv sampler. mTexturesIncompatibleWithSamplers[textureIndex] = mExecutable->getActiveYUVSamplers().test(textureIndex) && !texture->isYUV(); if (isWebGL()) { const Sampler *sampler = mSamplers[textureIndex].get(); const SamplerState &samplerState = sampler ? sampler->getSamplerState() : texture->getSamplerState(); if (!texture->getTextureState().compatibleWithSamplerFormatForWebGL( mExecutable->getSamplerFormatForTextureUnitIndex(textureIndex), samplerState)) { mTexturesIncompatibleWithSamplers[textureIndex] = true; } } } else { mTexturesIncompatibleWithSamplers[textureIndex] = false; } } ANGLE_INLINE void State::updateTextureBinding(const Context *context, size_t textureIndex, Texture *texture) { mCompleteTextureBindings[textureIndex].bind(texture); mActiveTexturesCache.reset(textureIndex); setActiveTextureDirty(textureIndex, texture); } ANGLE_INLINE bool State::hasConstantColor(GLenum sourceRGB, GLenum destRGB) const { return sourceRGB == GL_CONSTANT_COLOR || sourceRGB == GL_ONE_MINUS_CONSTANT_COLOR || destRGB == GL_CONSTANT_COLOR || destRGB == GL_ONE_MINUS_CONSTANT_COLOR; } ANGLE_INLINE bool State::hasConstantAlpha(GLenum sourceRGB, GLenum destRGB) const { return sourceRGB == GL_CONSTANT_ALPHA || sourceRGB == GL_ONE_MINUS_CONSTANT_ALPHA || destRGB == GL_CONSTANT_ALPHA || destRGB == GL_ONE_MINUS_CONSTANT_ALPHA; } const RasterizerState &State::getRasterizerState() const { return mRasterizer; } const DepthStencilState &State::getDepthStencilState() const { return mDepthStencil; } void State::setColorClearValue(float red, float green, float blue, float alpha) { mColorClearValue.red = red; mColorClearValue.green = green; mColorClearValue.blue = blue; mColorClearValue.alpha = alpha; mDirtyBits.set(DIRTY_BIT_CLEAR_COLOR); } void State::setDepthClearValue(float depth) { mDepthClearValue = depth; mDirtyBits.set(DIRTY_BIT_CLEAR_DEPTH); } void State::setStencilClearValue(int stencil) { mStencilClearValue = stencil; mDirtyBits.set(DIRTY_BIT_CLEAR_STENCIL); } void State::setColorMask(bool red, bool green, bool blue, bool alpha) { mBlendState.colorMaskRed = red; mBlendState.colorMaskGreen = green; mBlendState.colorMaskBlue = blue; mBlendState.colorMaskAlpha = alpha; mBlendStateExt.setColorMask(red, green, blue, alpha); mDirtyBits.set(DIRTY_BIT_COLOR_MASK); } void State::setColorMaskIndexed(bool red, bool green, bool blue, bool alpha, GLuint index) { mBlendStateExt.setColorMaskIndexed(index, red, green, blue, alpha); mDirtyBits.set(DIRTY_BIT_COLOR_MASK); } bool State::allActiveDrawBufferChannelsMasked() const { // Compare current color mask with all-disabled color mask, while ignoring disabled draw // buffers. return (mBlendStateExt.compareColorMask(0) & mDrawFramebuffer->getDrawBufferMask()).none(); } bool State::anyActiveDrawBufferChannelMasked() const { // Compare current color mask with all-enabled color mask, while ignoring disabled draw // buffers. return (mBlendStateExt.compareColorMask(mBlendStateExt.getAllColorMaskBits()) & mDrawFramebuffer->getDrawBufferMask()) .any(); } void State::setDepthMask(bool mask) { if (mDepthStencil.depthMask != mask) { mDepthStencil.depthMask = mask; mDirtyBits.set(DIRTY_BIT_DEPTH_MASK); } } void State::setRasterizerDiscard(bool enabled) { if (mRasterizer.rasterizerDiscard != enabled) { mRasterizer.rasterizerDiscard = enabled; mDirtyBits.set(DIRTY_BIT_RASTERIZER_DISCARD_ENABLED); } } void State::setCullFace(bool enabled) { if (mRasterizer.cullFace != enabled) { mRasterizer.cullFace = enabled; mDirtyBits.set(DIRTY_BIT_CULL_FACE_ENABLED); } } void State::setCullMode(CullFaceMode mode) { if (mRasterizer.cullMode != mode) { mRasterizer.cullMode = mode; mDirtyBits.set(DIRTY_BIT_CULL_FACE); } } void State::setFrontFace(GLenum front) { if (mRasterizer.frontFace != front) { mRasterizer.frontFace = front; mDirtyBits.set(DIRTY_BIT_FRONT_FACE); } } void State::setDepthTest(bool enabled) { if (mDepthStencil.depthTest != enabled) { mDepthStencil.depthTest = enabled; mDirtyBits.set(DIRTY_BIT_DEPTH_TEST_ENABLED); } } void State::setDepthFunc(GLenum depthFunc) { if (mDepthStencil.depthFunc != depthFunc) { mDepthStencil.depthFunc = depthFunc; mDirtyBits.set(DIRTY_BIT_DEPTH_FUNC); } } void State::setDepthRange(float zNear, float zFar) { if (mNearZ != zNear || mFarZ != zFar) { mNearZ = zNear; mFarZ = zFar; mDirtyBits.set(DIRTY_BIT_DEPTH_RANGE); } } void State::setClipControl(GLenum origin, GLenum depth) { bool updated = false; if (mClipControlOrigin != origin) { mClipControlOrigin = origin; updated = true; } if (mClipControlDepth != depth) { mClipControlDepth = depth; updated = true; } if (updated) { mDirtyBits.set(DIRTY_BIT_EXTENDED); mExtendedDirtyBits.set(EXTENDED_DIRTY_BIT_CLIP_CONTROL); } } void State::setBlend(bool enabled) { if (mSetBlendIndexedInvoked || mBlendState.blend != enabled) { mBlendState.blend = enabled; mSetBlendIndexedInvoked = false; mBlendStateExt.setEnabled(enabled); mDirtyBits.set(DIRTY_BIT_BLEND_ENABLED); } } void State::setBlendIndexed(bool enabled, GLuint index) { mSetBlendIndexedInvoked = true; mBlendStateExt.setEnabledIndexed(index, enabled); mDirtyBits.set(DIRTY_BIT_BLEND_ENABLED); } void State::setBlendFactors(GLenum sourceRGB, GLenum destRGB, GLenum sourceAlpha, GLenum destAlpha) { if (!mSetBlendFactorsIndexedInvoked && mBlendState.sourceBlendRGB == sourceRGB && mBlendState.destBlendRGB == destRGB && mBlendState.sourceBlendAlpha == sourceAlpha && mBlendState.destBlendAlpha == destAlpha) { return; } mBlendState.sourceBlendRGB = sourceRGB; mBlendState.destBlendRGB = destRGB; mBlendState.sourceBlendAlpha = sourceAlpha; mBlendState.destBlendAlpha = destAlpha; if (mNoSimultaneousConstantColorAndAlphaBlendFunc) { if (hasConstantColor(sourceRGB, destRGB)) { mBlendFuncConstantColorDrawBuffers.set(); } else { mBlendFuncConstantColorDrawBuffers.reset(); } if (hasConstantAlpha(sourceRGB, destRGB)) { mBlendFuncConstantAlphaDrawBuffers.set(); } else { mBlendFuncConstantAlphaDrawBuffers.reset(); } } mSetBlendFactorsIndexedInvoked = false; mBlendStateExt.setFactors(sourceRGB, destRGB, sourceAlpha, destAlpha); mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS); } void State::setBlendFactorsIndexed(GLenum sourceRGB, GLenum destRGB, GLenum sourceAlpha, GLenum destAlpha, GLuint index) { if (mNoSimultaneousConstantColorAndAlphaBlendFunc) { mBlendFuncConstantColorDrawBuffers.set(index, hasConstantColor(sourceRGB, destRGB)); mBlendFuncConstantAlphaDrawBuffers.set(index, hasConstantAlpha(sourceRGB, destRGB)); } mSetBlendFactorsIndexedInvoked = true; mBlendStateExt.setFactorsIndexed(index, sourceRGB, destRGB, sourceAlpha, destAlpha); mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS); } void State::setBlendColor(float red, float green, float blue, float alpha) { // In ES2 without render-to-float extensions, BlendColor clamps to [0,1] on store. // On ES3+, or with render-to-float exts enabled, it does not clamp on store. const bool isES2 = mClientVersion.major == 2; const bool hasFloatBlending = mExtensions.colorBufferFloatEXT || mExtensions.colorBufferHalfFloatEXT || mExtensions.colorBufferFloatRgbCHROMIUM || mExtensions.colorBufferFloatRgbaCHROMIUM; if ((isES2 && !hasFloatBlending) || mNoUnclampedBlendColor) { red = clamp01(red); green = clamp01(green); blue = clamp01(blue); alpha = clamp01(alpha); } if (mBlendColor.red != red || mBlendColor.green != green || mBlendColor.blue != blue || mBlendColor.alpha != alpha) { mBlendColor.red = red; mBlendColor.green = green; mBlendColor.blue = blue; mBlendColor.alpha = alpha; mDirtyBits.set(DIRTY_BIT_BLEND_COLOR); } } void State::setBlendEquation(GLenum rgbEquation, GLenum alphaEquation) { if (mSetBlendEquationsIndexedInvoked || mBlendState.blendEquationRGB != rgbEquation || mBlendState.blendEquationAlpha != alphaEquation) { mBlendState.blendEquationRGB = rgbEquation; mBlendState.blendEquationAlpha = alphaEquation; mSetBlendEquationsIndexedInvoked = false; mBlendStateExt.setEquations(rgbEquation, alphaEquation); mDirtyBits.set(DIRTY_BIT_BLEND_EQUATIONS); } } void State::setBlendEquationIndexed(GLenum rgbEquation, GLenum alphaEquation, GLuint index) { mSetBlendEquationsIndexedInvoked = true; mBlendStateExt.setEquationsIndexed(index, rgbEquation, alphaEquation); mDirtyBits.set(DIRTY_BIT_BLEND_EQUATIONS); } void State::setStencilTest(bool enabled) { if (mDepthStencil.stencilTest != enabled) { mDepthStencil.stencilTest = enabled; mDirtyBits.set(DIRTY_BIT_STENCIL_TEST_ENABLED); } } void State::setStencilParams(GLenum stencilFunc, GLint stencilRef, GLuint stencilMask) { if (mDepthStencil.stencilFunc != stencilFunc || mStencilRef != stencilRef || mDepthStencil.stencilMask != stencilMask) { mDepthStencil.stencilFunc = stencilFunc; mStencilRef = stencilRef; mDepthStencil.stencilMask = stencilMask; mDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_FRONT); } } void State::setStencilBackParams(GLenum stencilBackFunc, GLint stencilBackRef, GLuint stencilBackMask) { if (mDepthStencil.stencilBackFunc != stencilBackFunc || mStencilBackRef != stencilBackRef || mDepthStencil.stencilBackMask != stencilBackMask) { mDepthStencil.stencilBackFunc = stencilBackFunc; mStencilBackRef = stencilBackRef; mDepthStencil.stencilBackMask = stencilBackMask; mDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_BACK); } } void State::setStencilWritemask(GLuint stencilWritemask) { if (mDepthStencil.stencilWritemask != stencilWritemask) { mDepthStencil.stencilWritemask = stencilWritemask; mDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_FRONT); } } void State::setStencilBackWritemask(GLuint stencilBackWritemask) { if (mDepthStencil.stencilBackWritemask != stencilBackWritemask) { mDepthStencil.stencilBackWritemask = stencilBackWritemask; mDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_BACK); } } void State::setStencilOperations(GLenum stencilFail, GLenum stencilPassDepthFail, GLenum stencilPassDepthPass) { if (mDepthStencil.stencilFail != stencilFail || mDepthStencil.stencilPassDepthFail != stencilPassDepthFail || mDepthStencil.stencilPassDepthPass != stencilPassDepthPass) { mDepthStencil.stencilFail = stencilFail; mDepthStencil.stencilPassDepthFail = stencilPassDepthFail; mDepthStencil.stencilPassDepthPass = stencilPassDepthPass; mDirtyBits.set(DIRTY_BIT_STENCIL_OPS_FRONT); } } void State::setStencilBackOperations(GLenum stencilBackFail, GLenum stencilBackPassDepthFail, GLenum stencilBackPassDepthPass) { if (mDepthStencil.stencilBackFail != stencilBackFail || mDepthStencil.stencilBackPassDepthFail != stencilBackPassDepthFail || mDepthStencil.stencilBackPassDepthPass != stencilBackPassDepthPass) { mDepthStencil.stencilBackFail = stencilBackFail; mDepthStencil.stencilBackPassDepthFail = stencilBackPassDepthFail; mDepthStencil.stencilBackPassDepthPass = stencilBackPassDepthPass; mDirtyBits.set(DIRTY_BIT_STENCIL_OPS_BACK); } } void State::setPolygonOffsetFill(bool enabled) { if (mRasterizer.polygonOffsetFill != enabled) { mRasterizer.polygonOffsetFill = enabled; mDirtyBits.set(DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED); } } void State::setPolygonOffsetParams(GLfloat factor, GLfloat units) { // An application can pass NaN values here, so handle this gracefully mRasterizer.polygonOffsetFactor = factor != factor ? 0.0f : factor; mRasterizer.polygonOffsetUnits = units != units ? 0.0f : units; mDirtyBits.set(DIRTY_BIT_POLYGON_OFFSET); } void State::setSampleAlphaToCoverage(bool enabled) { if (mSampleAlphaToCoverage != enabled) { mSampleAlphaToCoverage = enabled; mDirtyBits.set(DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED); } } void State::setSampleCoverage(bool enabled) { if (mSampleCoverage != enabled) { mSampleCoverage = enabled; mDirtyBits.set(DIRTY_BIT_SAMPLE_COVERAGE_ENABLED); } } void State::setSampleCoverageParams(GLclampf value, bool invert) { mSampleCoverageValue = value; mSampleCoverageInvert = invert; mDirtyBits.set(DIRTY_BIT_SAMPLE_COVERAGE); } void State::setSampleMaskEnabled(bool enabled) { if (mSampleMask != enabled) { mSampleMask = enabled; mDirtyBits.set(DIRTY_BIT_SAMPLE_MASK_ENABLED); } } void State::setSampleMaskParams(GLuint maskNumber, GLbitfield mask) { ASSERT(maskNumber < mMaxSampleMaskWords); mSampleMaskValues[maskNumber] = mask; // TODO(jmadill): Use a child dirty bit if we ever use more than two words. mDirtyBits.set(DIRTY_BIT_SAMPLE_MASK); } void State::setSampleAlphaToOne(bool enabled) { if (mSampleAlphaToOne != enabled) { mSampleAlphaToOne = enabled; mDirtyBits.set(DIRTY_BIT_SAMPLE_ALPHA_TO_ONE); } } void State::setMultisampling(bool enabled) { if (mMultiSampling != enabled) { mMultiSampling = enabled; mDirtyBits.set(DIRTY_BIT_MULTISAMPLING); } } void State::setSampleShading(bool enabled) { if (mIsSampleShadingEnabled != enabled) { mIsSampleShadingEnabled = enabled; mMinSampleShading = (enabled) ? 1.0f : mMinSampleShading; mDirtyBits.set(DIRTY_BIT_SAMPLE_SHADING); } } void State::setMinSampleShading(float value) { value = gl::clamp01(value); if (mMinSampleShading != value) { mMinSampleShading = value; mDirtyBits.set(DIRTY_BIT_SAMPLE_SHADING); } } void State::setScissorTest(bool enabled) { if (mScissorTest != enabled) { mScissorTest = enabled; mDirtyBits.set(DIRTY_BIT_SCISSOR_TEST_ENABLED); } } void State::setScissorParams(GLint x, GLint y, GLsizei width, GLsizei height) { // Skip if same scissor info if (mScissor.x != x || mScissor.y != y || mScissor.width != width || mScissor.height != height) { mScissor.x = x; mScissor.y = y; mScissor.width = width; mScissor.height = height; mDirtyBits.set(DIRTY_BIT_SCISSOR); } } void State::setDither(bool enabled) { if (mRasterizer.dither != enabled) { mRasterizer.dither = enabled; mDirtyBits.set(DIRTY_BIT_DITHER_ENABLED); } } void State::setPrimitiveRestart(bool enabled) { if (mPrimitiveRestart != enabled) { mPrimitiveRestart = enabled; mDirtyBits.set(DIRTY_BIT_PRIMITIVE_RESTART_ENABLED); } } void State::setClipDistanceEnable(int idx, bool enable) { if (enable) { mClipDistancesEnabled.set(idx); } else { mClipDistancesEnabled.reset(idx); } mDirtyBits.set(DIRTY_BIT_EXTENDED); mExtendedDirtyBits.set(EXTENDED_DIRTY_BIT_CLIP_DISTANCES); } void State::setEnableFeature(GLenum feature, bool enabled) { switch (feature) { case GL_MULTISAMPLE_EXT: setMultisampling(enabled); return; case GL_SAMPLE_ALPHA_TO_ONE_EXT: setSampleAlphaToOne(enabled); return; case GL_CULL_FACE: setCullFace(enabled); return; case GL_POLYGON_OFFSET_FILL: setPolygonOffsetFill(enabled); return; case GL_SAMPLE_ALPHA_TO_COVERAGE: setSampleAlphaToCoverage(enabled); return; case GL_SAMPLE_COVERAGE: setSampleCoverage(enabled); return; case GL_SCISSOR_TEST: setScissorTest(enabled); return; case GL_STENCIL_TEST: setStencilTest(enabled); return; case GL_DEPTH_TEST: setDepthTest(enabled); return; case GL_BLEND: setBlend(enabled); return; case GL_DITHER: setDither(enabled); return; case GL_COLOR_LOGIC_OP: if (mClientVersion.major == 1) { // Handle logicOp in GLES1 through the GLES1 state management and emulation. // Otherwise this state could be set as part of ANGLE_logic_op. break; } setLogicOpEnabled(enabled); return; case GL_PRIMITIVE_RESTART_FIXED_INDEX: setPrimitiveRestart(enabled); return; case GL_RASTERIZER_DISCARD: setRasterizerDiscard(enabled); return; case GL_SAMPLE_MASK: setSampleMaskEnabled(enabled); return; case GL_DEBUG_OUTPUT_SYNCHRONOUS: mDebug.setOutputSynchronous(enabled); return; case GL_DEBUG_OUTPUT: mDebug.setOutputEnabled(enabled); return; case GL_FRAMEBUFFER_SRGB_EXT: setFramebufferSRGB(enabled); return; case GL_TEXTURE_RECTANGLE_ANGLE: mTextureRectangleEnabled = enabled; return; case GL_SAMPLE_SHADING: setSampleShading(enabled); return; // GL_APPLE_clip_distance/GL_EXT_clip_cull_distance case GL_CLIP_DISTANCE0_EXT: case GL_CLIP_DISTANCE1_EXT: case GL_CLIP_DISTANCE2_EXT: case GL_CLIP_DISTANCE3_EXT: case GL_CLIP_DISTANCE4_EXT: case GL_CLIP_DISTANCE5_EXT: case GL_CLIP_DISTANCE6_EXT: case GL_CLIP_DISTANCE7_EXT: // NOTE(hqle): These enums are conflicted with GLES1's enums, need // to do additional check here: if (mClientVersion.major >= 2) { setClipDistanceEnable(feature - GL_CLIP_DISTANCE0_EXT, enabled); return; } break; case GL_SHADING_RATE_PRESERVE_ASPECT_RATIO_QCOM: mShadingRatePreserveAspectRatio = enabled; return; } ASSERT(mClientVersion.major == 1); // GLES1 emulation. Need to separate from main switch due to conflict enum between // GL_CLIP_DISTANCE0_EXT & GL_CLIP_PLANE0 switch (feature) { case GL_ALPHA_TEST: mGLES1State.mAlphaTestEnabled = enabled; break; case GL_TEXTURE_2D: mGLES1State.mTexUnitEnables[mActiveSampler].set(TextureType::_2D, enabled); break; case GL_TEXTURE_CUBE_MAP: mGLES1State.mTexUnitEnables[mActiveSampler].set(TextureType::CubeMap, enabled); break; case GL_LIGHTING: mGLES1State.mLightingEnabled = enabled; break; case GL_LIGHT0: case GL_LIGHT1: case GL_LIGHT2: case GL_LIGHT3: case GL_LIGHT4: case GL_LIGHT5: case GL_LIGHT6: case GL_LIGHT7: mGLES1State.mLights[feature - GL_LIGHT0].enabled = enabled; break; case GL_NORMALIZE: mGLES1State.mNormalizeEnabled = enabled; break; case GL_RESCALE_NORMAL: mGLES1State.mRescaleNormalEnabled = enabled; break; case GL_COLOR_MATERIAL: mGLES1State.mColorMaterialEnabled = enabled; break; case GL_CLIP_PLANE0: case GL_CLIP_PLANE1: case GL_CLIP_PLANE2: case GL_CLIP_PLANE3: case GL_CLIP_PLANE4: case GL_CLIP_PLANE5: mGLES1State.mClipPlanes[feature - GL_CLIP_PLANE0].enabled = enabled; break; case GL_FOG: mGLES1State.mFogEnabled = enabled; break; case GL_POINT_SMOOTH: mGLES1State.mPointSmoothEnabled = enabled; break; case GL_LINE_SMOOTH: mGLES1State.mLineSmoothEnabled = enabled; break; case GL_POINT_SPRITE_OES: mGLES1State.mPointSpriteEnabled = enabled; break; case GL_COLOR_LOGIC_OP: mGLES1State.setLogicOpEnabled(enabled); break; default: UNREACHABLE(); } } void State::setEnableFeatureIndexed(GLenum feature, bool enabled, GLuint index) { switch (feature) { case GL_BLEND: setBlendIndexed(enabled, index); break; default: UNREACHABLE(); } } bool State::getEnableFeature(GLenum feature) const { switch (feature) { case GL_MULTISAMPLE_EXT: return isMultisamplingEnabled(); case GL_SAMPLE_ALPHA_TO_ONE_EXT: return isSampleAlphaToOneEnabled(); case GL_CULL_FACE: return isCullFaceEnabled(); case GL_POLYGON_OFFSET_FILL: return isPolygonOffsetFillEnabled(); case GL_SAMPLE_ALPHA_TO_COVERAGE: return isSampleAlphaToCoverageEnabled(); case GL_SAMPLE_COVERAGE: return isSampleCoverageEnabled(); case GL_SCISSOR_TEST: return isScissorTestEnabled(); case GL_STENCIL_TEST: return isStencilTestEnabled(); case GL_DEPTH_TEST: return isDepthTestEnabled(); case GL_BLEND: return isBlendEnabled(); case GL_DITHER: return isDitherEnabled(); case GL_COLOR_LOGIC_OP: if (mClientVersion.major == 1) { // Handle logicOp in GLES1 through the GLES1 state management and emulation. break; } return isLogicOpEnabled(); case GL_PRIMITIVE_RESTART_FIXED_INDEX: return isPrimitiveRestartEnabled(); case GL_RASTERIZER_DISCARD: return isRasterizerDiscardEnabled(); case GL_SAMPLE_MASK: return isSampleMaskEnabled(); case GL_DEBUG_OUTPUT_SYNCHRONOUS: return mDebug.isOutputSynchronous(); case GL_DEBUG_OUTPUT: return mDebug.isOutputEnabled(); case GL_BIND_GENERATES_RESOURCE_CHROMIUM: return isBindGeneratesResourceEnabled(); case GL_CLIENT_ARRAYS_ANGLE: return areClientArraysEnabled(); case GL_FRAMEBUFFER_SRGB_EXT: return getFramebufferSRGB(); case GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE: return mRobustResourceInit; case GL_PROGRAM_CACHE_ENABLED_ANGLE: return mProgramBinaryCacheEnabled; case GL_TEXTURE_RECTANGLE_ANGLE: return mTextureRectangleEnabled; case GL_SAMPLE_SHADING: return isSampleShadingEnabled(); // GL_APPLE_clip_distance/GL_EXT_clip_cull_distance case GL_CLIP_DISTANCE0_EXT: case GL_CLIP_DISTANCE1_EXT: case GL_CLIP_DISTANCE2_EXT: case GL_CLIP_DISTANCE3_EXT: case GL_CLIP_DISTANCE4_EXT: case GL_CLIP_DISTANCE5_EXT: case GL_CLIP_DISTANCE6_EXT: case GL_CLIP_DISTANCE7_EXT: if (mClientVersion.major >= 2) { // If GLES version is 1, the GL_CLIP_DISTANCE0_EXT enum will be used as // GL_CLIP_PLANE0 instead. return mClipDistancesEnabled.test(feature - GL_CLIP_DISTANCE0_EXT); } break; case GL_SHADING_RATE_PRESERVE_ASPECT_RATIO_QCOM: return mShadingRatePreserveAspectRatio; } ASSERT(mClientVersion.major == 1); switch (feature) { // GLES1 emulation case GL_ALPHA_TEST: return mGLES1State.mAlphaTestEnabled; case GL_VERTEX_ARRAY: return mGLES1State.mVertexArrayEnabled; case GL_NORMAL_ARRAY: return mGLES1State.mNormalArrayEnabled; case GL_COLOR_ARRAY: return mGLES1State.mColorArrayEnabled; case GL_POINT_SIZE_ARRAY_OES: return mGLES1State.mPointSizeArrayEnabled; case GL_TEXTURE_COORD_ARRAY: return mGLES1State.mTexCoordArrayEnabled[mGLES1State.mClientActiveTexture]; case GL_TEXTURE_2D: return mGLES1State.isTextureTargetEnabled(getActiveSampler(), TextureType::_2D); case GL_TEXTURE_CUBE_MAP: return mGLES1State.isTextureTargetEnabled(getActiveSampler(), TextureType::CubeMap); case GL_LIGHTING: return mGLES1State.mLightingEnabled; case GL_LIGHT0: case GL_LIGHT1: case GL_LIGHT2: case GL_LIGHT3: case GL_LIGHT4: case GL_LIGHT5: case GL_LIGHT6: case GL_LIGHT7: return mGLES1State.mLights[feature - GL_LIGHT0].enabled; case GL_NORMALIZE: return mGLES1State.mNormalizeEnabled; case GL_RESCALE_NORMAL: return mGLES1State.mRescaleNormalEnabled; case GL_COLOR_MATERIAL: return mGLES1State.mColorMaterialEnabled; case GL_CLIP_PLANE0: case GL_CLIP_PLANE1: case GL_CLIP_PLANE2: case GL_CLIP_PLANE3: case GL_CLIP_PLANE4: case GL_CLIP_PLANE5: return mGLES1State.mClipPlanes[feature - GL_CLIP_PLANE0].enabled; case GL_FOG: return mGLES1State.mFogEnabled; case GL_POINT_SMOOTH: return mGLES1State.mPointSmoothEnabled; case GL_LINE_SMOOTH: return mGLES1State.mLineSmoothEnabled; case GL_POINT_SPRITE_OES: return mGLES1State.mPointSpriteEnabled; case GL_COLOR_LOGIC_OP: return mGLES1State.mLogicOpEnabled; default: UNREACHABLE(); return false; } } bool State::getEnableFeatureIndexed(GLenum feature, GLuint index) const { switch (feature) { case GL_BLEND: return isBlendEnabledIndexed(index); default: UNREACHABLE(); return false; } } void State::setLineWidth(GLfloat width) { mLineWidth = width; mDirtyBits.set(DIRTY_BIT_LINE_WIDTH); } void State::setGenerateMipmapHint(GLenum hint) { mGenerateMipmapHint = hint; mDirtyBits.set(DIRTY_BIT_EXTENDED); mExtendedDirtyBits.set(EXTENDED_DIRTY_BIT_MIPMAP_GENERATION_HINT); } GLenum State::getGenerateMipmapHint() const { return mGenerateMipmapHint; } void State::setTextureFilteringHint(GLenum hint) { mTextureFilteringHint = hint; // Note: we don't add a dirty bit for this flag as it's not expected to be toggled at // runtime. } GLenum State::getTextureFilteringHint() const { return mTextureFilteringHint; } void State::setFragmentShaderDerivativeHint(GLenum hint) { mFragmentShaderDerivativeHint = hint; mDirtyBits.set(DIRTY_BIT_EXTENDED); mExtendedDirtyBits.set(EXTENDED_DIRTY_BIT_SHADER_DERIVATIVE_HINT); // TODO: Propagate the hint to shader translator so we can write // ddx, ddx_coarse, or ddx_fine depending on the hint. // Ignore for now. It is valid for implementations to ignore hint. } void State::setViewportParams(GLint x, GLint y, GLsizei width, GLsizei height) { // [OpenGL ES 2.0.25] section 2.12.1 page 45: // Viewport width and height are clamped to implementation-dependent maximums when specified. width = std::min(width, mCaps.maxViewportWidth); height = std::min(height, mCaps.maxViewportHeight); // Skip if same viewport info if (mViewport.x != x || mViewport.y != y || mViewport.width != width || mViewport.height != height) { mViewport.x = x; mViewport.y = y; mViewport.width = width; mViewport.height = height; mDirtyBits.set(DIRTY_BIT_VIEWPORT); } } void State::setActiveSampler(unsigned int active) { mActiveSampler = active; } void State::setSamplerTexture(const Context *context, TextureType type, Texture *texture) { if (mExecutable && mExecutable->getActiveSamplersMask()[mActiveSampler] && IsTextureCompatibleWithSampler(type, mExecutable->getActiveSamplerTypes()[mActiveSampler])) { updateTextureBinding(context, mActiveSampler, texture); } mSamplerTextures[type][mActiveSampler].set(context, texture); mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); } Texture *State::getTargetTexture(TextureType type) const { return getSamplerTexture(static_cast(mActiveSampler), type); } TextureID State::getSamplerTextureId(unsigned int sampler, TextureType type) const { ASSERT(sampler < mSamplerTextures[type].size()); return mSamplerTextures[type][sampler].id(); } void State::detachTexture(const Context *context, const TextureMap &zeroTextures, TextureID texture) { // Textures have a detach method on State rather than a simple // removeBinding, because the zero/null texture objects are managed // separately, and don't have to go through the Context's maps or // the ResourceManager. // [OpenGL ES 2.0.24] section 3.8 page 84: // If a texture object is deleted, it is as if all texture units which are bound to that texture // object are rebound to texture object zero for (TextureType type : angle::AllEnums()) { TextureBindingVector &textureVector = mSamplerTextures[type]; for (size_t bindingIndex = 0; bindingIndex < textureVector.size(); ++bindingIndex) { BindingPointer &binding = textureVector[bindingIndex]; if (binding.id() == texture) { // Zero textures are the "default" textures instead of NULL Texture *zeroTexture = zeroTextures[type].get(); ASSERT(zeroTexture != nullptr); if (mCompleteTextureBindings[bindingIndex].getSubject() == binding.get()) { updateTextureBinding(context, bindingIndex, zeroTexture); } binding.set(context, zeroTexture); } } } for (auto &bindingImageUnit : mImageUnits) { if (bindingImageUnit.texture.id() == texture) { bindingImageUnit.texture.set(context, nullptr); bindingImageUnit.level = 0; bindingImageUnit.layered = false; bindingImageUnit.layer = 0; bindingImageUnit.access = GL_READ_ONLY; bindingImageUnit.format = GL_R32UI; } } // [OpenGL ES 2.0.24] section 4.4 page 112: // If a texture object is deleted while its image is attached to the currently bound // framebuffer, then it is as if Texture2DAttachment had been called, with a texture of 0, for // each attachment point to which this image was attached in the currently bound framebuffer. if (mReadFramebuffer && mReadFramebuffer->detachTexture(context, texture)) { mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); } if (mDrawFramebuffer && mDrawFramebuffer->detachTexture(context, texture)) { setDrawFramebufferDirty(); } } void State::initializeZeroTextures(const Context *context, const TextureMap &zeroTextures) { for (TextureType type : angle::AllEnums()) { for (size_t textureUnit = 0; textureUnit < mSamplerTextures[type].size(); ++textureUnit) { mSamplerTextures[type][textureUnit].set(context, zeroTextures[type].get()); } } } void State::invalidateTextureBindings(TextureType type) { mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); } void State::setSamplerBinding(const Context *context, GLuint textureUnit, Sampler *sampler) { if (mSamplers[textureUnit].get() == sampler) { return; } mSamplers[textureUnit].set(context, sampler); mDirtyBits.set(DIRTY_BIT_SAMPLER_BINDINGS); // This is overly conservative as it assumes the sampler has never been bound. setSamplerDirty(textureUnit); onActiveTextureChange(context, textureUnit); } void State::detachSampler(const Context *context, SamplerID sampler) { // [OpenGL ES 3.0.2] section 3.8.2 pages 123-124: // If a sampler object that is currently bound to one or more texture units is // deleted, it is as though BindSampler is called once for each texture unit to // which the sampler is bound, with unit set to the texture unit and sampler set to zero. for (size_t i = 0; i < mSamplers.size(); i++) { if (mSamplers[i].id() == sampler) { setSamplerBinding(context, static_cast(i), nullptr); } } } void State::setRenderbufferBinding(const Context *context, Renderbuffer *renderbuffer) { mRenderbuffer.set(context, renderbuffer); mDirtyBits.set(DIRTY_BIT_RENDERBUFFER_BINDING); } void State::detachRenderbuffer(const Context *context, RenderbufferID renderbuffer) { // [OpenGL ES 2.0.24] section 4.4 page 109: // If a renderbuffer that is currently bound to RENDERBUFFER is deleted, it is as though // BindRenderbuffer had been executed with the target RENDERBUFFER and name of zero. if (mRenderbuffer.id() == renderbuffer) { setRenderbufferBinding(context, nullptr); } // [OpenGL ES 2.0.24] section 4.4 page 111: // If a renderbuffer object is deleted while its image is attached to the currently bound // framebuffer, then it is as if FramebufferRenderbuffer had been called, with a renderbuffer of // 0, for each attachment point to which this image was attached in the currently bound // framebuffer. Framebuffer *readFramebuffer = mReadFramebuffer; Framebuffer *drawFramebuffer = mDrawFramebuffer; if (readFramebuffer && readFramebuffer->detachRenderbuffer(context, renderbuffer)) { mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); } if (drawFramebuffer && drawFramebuffer != readFramebuffer) { if (drawFramebuffer->detachRenderbuffer(context, renderbuffer)) { setDrawFramebufferDirty(); } } } void State::setReadFramebufferBinding(Framebuffer *framebuffer) { if (mReadFramebuffer == framebuffer) return; mReadFramebuffer = framebuffer; mDirtyBits.set(DIRTY_BIT_READ_FRAMEBUFFER_BINDING); if (mReadFramebuffer && mReadFramebuffer->hasAnyDirtyBit()) { mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); } } void State::setDrawFramebufferBinding(Framebuffer *framebuffer) { if (mDrawFramebuffer == framebuffer) return; mDrawFramebuffer = framebuffer; mDirtyBits.set(DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); if (mDrawFramebuffer) { mDrawFramebuffer->setWriteControlMode(getFramebufferSRGB() ? SrgbWriteControlMode::Default : SrgbWriteControlMode::Linear); if (mDrawFramebuffer->hasAnyDirtyBit()) { mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); } if (mRobustResourceInit && mDrawFramebuffer->hasResourceThatNeedsInit()) { mDirtyObjects.set(DIRTY_OBJECT_DRAW_ATTACHMENTS); } } } Framebuffer *State::getTargetFramebuffer(GLenum target) const { switch (target) { case GL_READ_FRAMEBUFFER_ANGLE: return mReadFramebuffer; case GL_DRAW_FRAMEBUFFER_ANGLE: case GL_FRAMEBUFFER: return mDrawFramebuffer; default: UNREACHABLE(); return nullptr; } } Framebuffer *State::getDefaultFramebuffer() const { return mFramebufferManager->getDefaultFramebuffer(); } bool State::removeReadFramebufferBinding(FramebufferID framebuffer) { if (mReadFramebuffer != nullptr && mReadFramebuffer->id() == framebuffer) { setReadFramebufferBinding(nullptr); return true; } return false; } bool State::removeDrawFramebufferBinding(FramebufferID framebuffer) { if (mReadFramebuffer != nullptr && mDrawFramebuffer->id() == framebuffer) { setDrawFramebufferBinding(nullptr); return true; } return false; } void State::setVertexArrayBinding(const Context *context, VertexArray *vertexArray) { if (mVertexArray == vertexArray) { return; } if (mVertexArray) { mVertexArray->onBindingChanged(context, -1); } if (vertexArray) { vertexArray->onBindingChanged(context, 1); } mVertexArray = vertexArray; mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING); if (mVertexArray && mVertexArray->hasAnyDirtyBit()) { mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } } bool State::removeVertexArrayBinding(const Context *context, VertexArrayID vertexArray) { if (mVertexArray && mVertexArray->id().value == vertexArray.value) { mVertexArray->onBindingChanged(context, -1); mVertexArray = nullptr; mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING); mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); return true; } return false; } VertexArrayID State::getVertexArrayId() const { ASSERT(mVertexArray != nullptr); return mVertexArray->id(); } void State::bindVertexBuffer(const Context *context, GLuint bindingIndex, Buffer *boundBuffer, GLintptr offset, GLsizei stride) { getVertexArray()->bindVertexBuffer(context, bindingIndex, boundBuffer, offset, stride); mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } void State::setVertexAttribFormat(GLuint attribIndex, GLint size, VertexAttribType type, bool normalized, bool pureInteger, GLuint relativeOffset) { getVertexArray()->setVertexAttribFormat(attribIndex, size, type, normalized, pureInteger, relativeOffset); mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } void State::setVertexBindingDivisor(const Context *context, GLuint bindingIndex, GLuint divisor) { getVertexArray()->setVertexBindingDivisor(context, bindingIndex, divisor); mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } angle::Result State::setProgram(const Context *context, Program *newProgram) { if (newProgram && !newProgram->isLinked()) { // Protect against applications that disable validation and try to use a program that was // not successfully linked. WARN() << "Attempted to use a program that was not successfully linked"; return angle::Result::Continue; } if (mProgram != newProgram) { if (mProgram) { unsetActiveTextures(mExecutable->getActiveSamplersMask()); mProgram->release(context); } mProgram = newProgram; mExecutable = nullptr; if (mProgram) { mExecutable = &mProgram->getExecutable(); newProgram->addRef(); ANGLE_TRY(onProgramExecutableChange(context, newProgram)); } else if (mProgramPipeline.get()) { mExecutable = &mProgramPipeline->getExecutable(); ANGLE_TRY(onProgramPipelineExecutableChange(context)); } // Note that rendering is undefined if glUseProgram(0) is called. But ANGLE will generate // an error if the app tries to draw in this case. mDirtyBits.set(DIRTY_BIT_PROGRAM_BINDING); } return angle::Result::Continue; } void State::setTransformFeedbackBinding(const Context *context, TransformFeedback *transformFeedback) { if (transformFeedback == mTransformFeedback.get()) return; if (mTransformFeedback.get()) mTransformFeedback->onBindingChanged(context, false); mTransformFeedback.set(context, transformFeedback); if (mTransformFeedback.get()) mTransformFeedback->onBindingChanged(context, true); mDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING); } bool State::removeTransformFeedbackBinding(const Context *context, TransformFeedbackID transformFeedback) { if (mTransformFeedback.id() == transformFeedback) { if (mTransformFeedback.get()) mTransformFeedback->onBindingChanged(context, false); mTransformFeedback.set(context, nullptr); return true; } return false; } angle::Result State::setProgramPipelineBinding(const Context *context, ProgramPipeline *pipeline) { if (mProgramPipeline.get() == pipeline) { return angle::Result::Continue; } if (mProgramPipeline.get()) { unsetActiveTextures(mProgramPipeline->getExecutable().getActiveSamplersMask()); } mProgramPipeline.set(context, pipeline); mDirtyBits.set(DIRTY_BIT_PROGRAM_BINDING); // A bound Program always overrides the ProgramPipeline, so only update the // current ProgramExecutable if there isn't currently a Program bound. if (!mProgram) { if (mProgramPipeline.get()) { mExecutable = &mProgramPipeline->getExecutable(); ANGLE_TRY(onProgramPipelineExecutableChange(context)); } else { mExecutable = nullptr; } } return angle::Result::Continue; } void State::detachProgramPipeline(const Context *context, ProgramPipelineID pipeline) { mProgramPipeline.set(context, nullptr); // A bound Program always overrides the ProgramPipeline, so only update the // current ProgramExecutable if there isn't currently a Program bound. if (!mProgram) { mExecutable = nullptr; } } bool State::isQueryActive(QueryType type) const { const Query *query = mActiveQueries[type].get(); if (query != nullptr) { return true; } QueryType alternativeType; if (GetAlternativeQueryType(type, &alternativeType)) { query = mActiveQueries[alternativeType].get(); return query != nullptr; } return false; } bool State::isQueryActive(Query *query) const { for (auto &queryPointer : mActiveQueries) { if (queryPointer.get() == query) { return true; } } return false; } void State::setActiveQuery(const Context *context, QueryType type, Query *query) { mActiveQueries[type].set(context, query); } QueryID State::getActiveQueryId(QueryType type) const { const Query *query = getActiveQuery(type); if (query) { return query->id(); } return {0}; } Query *State::getActiveQuery(QueryType type) const { return mActiveQueries[type].get(); } angle::Result State::setIndexedBufferBinding(const Context *context, BufferBinding target, GLuint index, Buffer *buffer, GLintptr offset, GLsizeiptr size) { setBufferBinding(context, target, buffer); switch (target) { case BufferBinding::TransformFeedback: ANGLE_TRY(mTransformFeedback->bindIndexedBuffer(context, index, buffer, offset, size)); setBufferBinding(context, target, buffer); break; case BufferBinding::Uniform: mBoundUniformBuffersMask.set(index, buffer != nullptr); UpdateIndexedBufferBinding(context, &mUniformBuffers[index], buffer, target, offset, size); break; case BufferBinding::AtomicCounter: mBoundAtomicCounterBuffersMask.set(index, buffer != nullptr); UpdateIndexedBufferBinding(context, &mAtomicCounterBuffers[index], buffer, target, offset, size); break; case BufferBinding::ShaderStorage: mBoundShaderStorageBuffersMask.set(index, buffer != nullptr); UpdateIndexedBufferBinding(context, &mShaderStorageBuffers[index], buffer, target, offset, size); break; default: UNREACHABLE(); break; } return angle::Result::Continue; } const OffsetBindingPointer &State::getIndexedUniformBuffer(size_t index) const { ASSERT(index < mUniformBuffers.size()); return mUniformBuffers[index]; } const OffsetBindingPointer &State::getIndexedAtomicCounterBuffer(size_t index) const { ASSERT(index < mAtomicCounterBuffers.size()); return mAtomicCounterBuffers[index]; } const OffsetBindingPointer &State::getIndexedShaderStorageBuffer(size_t index) const { ASSERT(index < mShaderStorageBuffers.size()); return mShaderStorageBuffers[index]; } angle::Result State::detachBuffer(Context *context, const Buffer *buffer) { BufferID bufferID = buffer->id(); for (gl::BufferBinding target : angle::AllEnums()) { if (mBoundBuffers[target].id() == bufferID) { UpdateBufferBinding(context, &mBoundBuffers[target], nullptr, target); } } TransformFeedback *curTransformFeedback = getCurrentTransformFeedback(); if (curTransformFeedback) { ANGLE_TRY(curTransformFeedback->detachBuffer(context, bufferID)); context->getStateCache().onActiveTransformFeedbackChange(context); } if (getVertexArray()->detachBuffer(context, bufferID)) { mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); context->getStateCache().onVertexArrayStateChange(context); } for (size_t uniformBufferIndex : mBoundUniformBuffersMask) { OffsetBindingPointer &binding = mUniformBuffers[uniformBufferIndex]; if (binding.id() == bufferID) { UpdateIndexedBufferBinding(context, &binding, nullptr, BufferBinding::Uniform, 0, 0); mBoundUniformBuffersMask.reset(uniformBufferIndex); } } for (size_t atomicCounterBufferIndex : mBoundAtomicCounterBuffersMask) { OffsetBindingPointer &binding = mAtomicCounterBuffers[atomicCounterBufferIndex]; if (binding.id() == bufferID) { UpdateIndexedBufferBinding(context, &binding, nullptr, BufferBinding::AtomicCounter, 0, 0); mBoundAtomicCounterBuffersMask.reset(atomicCounterBufferIndex); } } for (size_t shaderStorageBufferIndex : mBoundShaderStorageBuffersMask) { OffsetBindingPointer &binding = mShaderStorageBuffers[shaderStorageBufferIndex]; if (binding.id() == bufferID) { UpdateIndexedBufferBinding(context, &binding, nullptr, BufferBinding::ShaderStorage, 0, 0); mBoundShaderStorageBuffersMask.reset(shaderStorageBufferIndex); } } return angle::Result::Continue; } void State::setEnableVertexAttribArray(unsigned int attribNum, bool enabled) { getVertexArray()->enableAttribute(attribNum, enabled); mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } void State::setVertexAttribf(GLuint index, const GLfloat values[4]) { ASSERT(static_cast(index) < mVertexAttribCurrentValues.size()); mVertexAttribCurrentValues[index].setFloatValues(values); mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES); mDirtyCurrentValues.set(index); SetComponentTypeMask(ComponentType::Float, index, &mCurrentValuesTypeMask); } void State::setVertexAttribu(GLuint index, const GLuint values[4]) { ASSERT(static_cast(index) < mVertexAttribCurrentValues.size()); mVertexAttribCurrentValues[index].setUnsignedIntValues(values); mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES); mDirtyCurrentValues.set(index); SetComponentTypeMask(ComponentType::UnsignedInt, index, &mCurrentValuesTypeMask); } void State::setVertexAttribi(GLuint index, const GLint values[4]) { ASSERT(static_cast(index) < mVertexAttribCurrentValues.size()); mVertexAttribCurrentValues[index].setIntValues(values); mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES); mDirtyCurrentValues.set(index); SetComponentTypeMask(ComponentType::Int, index, &mCurrentValuesTypeMask); } void State::setVertexAttribDivisor(const Context *context, GLuint index, GLuint divisor) { getVertexArray()->setVertexAttribDivisor(context, index, divisor); mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); } const void *State::getVertexAttribPointer(unsigned int attribNum) const { return getVertexArray()->getVertexAttribute(attribNum).pointer; } void State::setPackAlignment(GLint alignment) { mPack.alignment = alignment; mDirtyBits.set(DIRTY_BIT_PACK_STATE); } void State::setPackReverseRowOrder(bool reverseRowOrder) { mPack.reverseRowOrder = reverseRowOrder; mDirtyBits.set(DIRTY_BIT_PACK_STATE); } void State::setPackRowLength(GLint rowLength) { mPack.rowLength = rowLength; mDirtyBits.set(DIRTY_BIT_PACK_STATE); } void State::setPackSkipRows(GLint skipRows) { mPack.skipRows = skipRows; mDirtyBits.set(DIRTY_BIT_PACK_STATE); } void State::setPackSkipPixels(GLint skipPixels) { mPack.skipPixels = skipPixels; mDirtyBits.set(DIRTY_BIT_PACK_STATE); } void State::setUnpackAlignment(GLint alignment) { mUnpack.alignment = alignment; mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); } void State::setUnpackRowLength(GLint rowLength) { mUnpack.rowLength = rowLength; mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); } void State::setUnpackImageHeight(GLint imageHeight) { mUnpack.imageHeight = imageHeight; mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); } void State::setUnpackSkipImages(GLint skipImages) { mUnpack.skipImages = skipImages; mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); } void State::setUnpackSkipRows(GLint skipRows) { mUnpack.skipRows = skipRows; mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); } void State::setUnpackSkipPixels(GLint skipPixels) { mUnpack.skipPixels = skipPixels; mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); } void State::setCoverageModulation(GLenum components) { if (mCoverageModulation != components) { mCoverageModulation = components; mDirtyBits.set(DIRTY_BIT_COVERAGE_MODULATION); } } void State::setFramebufferSRGB(bool sRGB) { if (mFramebufferSRGB != sRGB) { mFramebufferSRGB = sRGB; mDirtyBits.set(DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE); setDrawFramebufferDirty(); } } void State::setMaxShaderCompilerThreads(GLuint count) { mMaxShaderCompilerThreads = count; } void State::setPatchVertices(GLuint value) { if (mPatchVertices != value) { mPatchVertices = value; mDirtyBits.set(DIRTY_BIT_PATCH_VERTICES); } } void State::setPixelLocalStorageActive(bool active) { mPixelLocalStorageActive = active; } void State::setShadingRate(GLenum rate) { mShadingRate = FromGLenum(rate); mDirtyBits.set(DIRTY_BIT_EXTENDED); mExtendedDirtyBits.set(EXTENDED_DIRTY_BIT_SHADING_RATE); } void State::getBooleanv(GLenum pname, GLboolean *params) const { switch (pname) { case GL_SAMPLE_COVERAGE_INVERT: *params = mSampleCoverageInvert; break; case GL_DEPTH_WRITEMASK: *params = mDepthStencil.depthMask; break; case GL_COLOR_WRITEMASK: { // non-indexed get returns the state of draw buffer zero bool r, g, b, a; mBlendStateExt.getColorMaskIndexed(0, &r, &g, &b, &a); params[0] = r; params[1] = g; params[2] = b; params[3] = a; break; } case GL_CULL_FACE: *params = mRasterizer.cullFace; break; case GL_POLYGON_OFFSET_FILL: *params = mRasterizer.polygonOffsetFill; break; case GL_SAMPLE_ALPHA_TO_COVERAGE: *params = mSampleAlphaToCoverage; break; case GL_SAMPLE_COVERAGE: *params = mSampleCoverage; break; case GL_SAMPLE_MASK: *params = mSampleMask; break; case GL_SCISSOR_TEST: *params = mScissorTest; break; case GL_STENCIL_TEST: *params = mDepthStencil.stencilTest; break; case GL_DEPTH_TEST: *params = mDepthStencil.depthTest; break; case GL_BLEND: // non-indexed get returns the state of draw buffer zero *params = mBlendStateExt.getEnabledMask().test(0); break; case GL_DITHER: *params = mRasterizer.dither; break; case GL_COLOR_LOGIC_OP: ASSERT(mClientVersion.major > 1); *params = mLogicOpEnabled; break; case GL_TRANSFORM_FEEDBACK_ACTIVE: *params = getCurrentTransformFeedback()->isActive() ? GL_TRUE : GL_FALSE; break; case GL_TRANSFORM_FEEDBACK_PAUSED: *params = getCurrentTransformFeedback()->isPaused() ? GL_TRUE : GL_FALSE; break; case GL_PRIMITIVE_RESTART_FIXED_INDEX: *params = mPrimitiveRestart; break; case GL_RASTERIZER_DISCARD: *params = isRasterizerDiscardEnabled() ? GL_TRUE : GL_FALSE; break; case GL_DEBUG_OUTPUT_SYNCHRONOUS: *params = mDebug.isOutputSynchronous() ? GL_TRUE : GL_FALSE; break; case GL_DEBUG_OUTPUT: *params = mDebug.isOutputEnabled() ? GL_TRUE : GL_FALSE; break; case GL_MULTISAMPLE_EXT: *params = mMultiSampling; break; case GL_SAMPLE_ALPHA_TO_ONE_EXT: *params = mSampleAlphaToOne; break; case GL_BIND_GENERATES_RESOURCE_CHROMIUM: *params = isBindGeneratesResourceEnabled() ? GL_TRUE : GL_FALSE; break; case GL_CLIENT_ARRAYS_ANGLE: *params = areClientArraysEnabled() ? GL_TRUE : GL_FALSE; break; case GL_FRAMEBUFFER_SRGB_EXT: *params = getFramebufferSRGB() ? GL_TRUE : GL_FALSE; break; case GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE: *params = mRobustResourceInit ? GL_TRUE : GL_FALSE; break; case GL_PROGRAM_CACHE_ENABLED_ANGLE: *params = mProgramBinaryCacheEnabled ? GL_TRUE : GL_FALSE; break; case GL_TEXTURE_RECTANGLE_ANGLE: *params = mTextureRectangleEnabled ? GL_TRUE : GL_FALSE; break; case GL_LIGHT_MODEL_TWO_SIDE: *params = IsLightModelTwoSided(&mGLES1State); break; case GL_SAMPLE_SHADING: *params = mIsSampleShadingEnabled; break; case GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED: *params = isPrimitiveRestartEnabled() && getExtensions().tessellationShaderEXT; break; // 2.2.2 Data Conversions For State Query Commands, in GLES 3.2 spec. // If a command returning boolean data is called, such as GetBooleanv, a floating-point or // integer value converts to FALSE if and only if it is zero. Otherwise it converts to TRUE. // GL_EXT_clip_control case GL_CLIP_ORIGIN_EXT: *params = GL_TRUE; break; case GL_CLIP_DEPTH_MODE_EXT: *params = GL_TRUE; break; case GL_ROBUST_FRAGMENT_SHADER_OUTPUT_ANGLE: *params = mExtensions.robustFragmentShaderOutputANGLE ? GL_TRUE : GL_FALSE; break; // GL_ANGLE_shader_pixel_local_storage case GL_PIXEL_LOCAL_STORAGE_ACTIVE_ANGLE: *params = mPixelLocalStorageActive ? GL_TRUE : GL_FALSE; break; default: UNREACHABLE(); break; } } void State::getFloatv(GLenum pname, GLfloat *params) const { // Please note: DEPTH_CLEAR_VALUE is included in our internal getFloatv implementation // because it is stored as a float, despite the fact that the GL ES 2.0 spec names // GetIntegerv as its native query function. As it would require conversion in any // case, this should make no difference to the calling application. switch (pname) { case GL_LINE_WIDTH: *params = mLineWidth; break; case GL_SAMPLE_COVERAGE_VALUE: *params = mSampleCoverageValue; break; case GL_DEPTH_CLEAR_VALUE: *params = mDepthClearValue; break; case GL_POLYGON_OFFSET_FACTOR: *params = mRasterizer.polygonOffsetFactor; break; case GL_POLYGON_OFFSET_UNITS: *params = mRasterizer.polygonOffsetUnits; break; case GL_DEPTH_RANGE: params[0] = mNearZ; params[1] = mFarZ; break; case GL_COLOR_CLEAR_VALUE: params[0] = mColorClearValue.red; params[1] = mColorClearValue.green; params[2] = mColorClearValue.blue; params[3] = mColorClearValue.alpha; break; case GL_BLEND_COLOR: params[0] = mBlendColor.red; params[1] = mBlendColor.green; params[2] = mBlendColor.blue; params[3] = mBlendColor.alpha; break; case GL_MULTISAMPLE_EXT: *params = static_cast(mMultiSampling); break; case GL_SAMPLE_ALPHA_TO_ONE_EXT: *params = static_cast(mSampleAlphaToOne); break; case GL_COVERAGE_MODULATION_CHROMIUM: params[0] = static_cast(mCoverageModulation); break; case GL_ALPHA_TEST_REF: *params = mGLES1State.mAlphaTestRef; break; case GL_CURRENT_COLOR: { const auto &color = mGLES1State.mCurrentColor; params[0] = color.red; params[1] = color.green; params[2] = color.blue; params[3] = color.alpha; break; } case GL_CURRENT_NORMAL: { const auto &normal = mGLES1State.mCurrentNormal; params[0] = normal[0]; params[1] = normal[1]; params[2] = normal[2]; break; } case GL_CURRENT_TEXTURE_COORDS: { const auto &texcoord = mGLES1State.mCurrentTextureCoords[mActiveSampler]; params[0] = texcoord.s; params[1] = texcoord.t; params[2] = texcoord.r; params[3] = texcoord.q; break; } case GL_MODELVIEW_MATRIX: memcpy(params, mGLES1State.mModelviewMatrices.back().constData(), 16 * sizeof(GLfloat)); break; case GL_PROJECTION_MATRIX: memcpy(params, mGLES1State.mProjectionMatrices.back().constData(), 16 * sizeof(GLfloat)); break; case GL_TEXTURE_MATRIX: memcpy(params, mGLES1State.mTextureMatrices[mActiveSampler].back().constData(), 16 * sizeof(GLfloat)); break; case GL_LIGHT_MODEL_AMBIENT: GetLightModelParameters(&mGLES1State, pname, params); break; case GL_FOG_MODE: case GL_FOG_DENSITY: case GL_FOG_START: case GL_FOG_END: case GL_FOG_COLOR: GetFogParameters(&mGLES1State, pname, params); break; case GL_POINT_SIZE: GetPointSize(&mGLES1State, params); break; case GL_POINT_SIZE_MIN: case GL_POINT_SIZE_MAX: case GL_POINT_FADE_THRESHOLD_SIZE: case GL_POINT_DISTANCE_ATTENUATION: GetPointParameter(&mGLES1State, FromGLenum(pname), params); break; case GL_MIN_SAMPLE_SHADING_VALUE: *params = mMinSampleShading; break; // 2.2.2 Data Conversions For State Query Commands, in GLES 3.2 spec. // If a command returning floating-point data is called, such as GetFloatv, ... An integer // value is coerced to floating-point. case GL_CLIP_ORIGIN_EXT: *params = static_cast(mClipControlOrigin); break; case GL_CLIP_DEPTH_MODE_EXT: *params = static_cast(mClipControlDepth); break; default: UNREACHABLE(); break; } } angle::Result State::getIntegerv(const Context *context, GLenum pname, GLint *params) const { if (pname >= GL_DRAW_BUFFER0_EXT && pname <= GL_DRAW_BUFFER15_EXT) { size_t drawBuffer = (pname - GL_DRAW_BUFFER0_EXT); ASSERT(drawBuffer < static_cast(mCaps.maxDrawBuffers)); Framebuffer *framebuffer = mDrawFramebuffer; // The default framebuffer may have fewer draw buffer states than a user-created one. The // user is always allowed to query up to GL_MAX_DRAWBUFFERS so just return GL_NONE here if // the draw buffer is out of range for this framebuffer. *params = drawBuffer < framebuffer->getDrawbufferStateCount() ? framebuffer->getDrawBufferState(drawBuffer) : GL_NONE; return angle::Result::Continue; } // Please note: DEPTH_CLEAR_VALUE is not included in our internal getIntegerv implementation // because it is stored as a float, despite the fact that the GL ES 2.0 spec names // GetIntegerv as its native query function. As it would require conversion in any // case, this should make no difference to the calling application. You may find it in // State::getFloatv. switch (pname) { case GL_ARRAY_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::Array].id().value; break; case GL_DRAW_INDIRECT_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::DrawIndirect].id().value; break; case GL_ELEMENT_ARRAY_BUFFER_BINDING: { Buffer *elementArrayBuffer = getVertexArray()->getElementArrayBuffer(); *params = elementArrayBuffer ? elementArrayBuffer->id().value : 0; break; } case GL_DRAW_FRAMEBUFFER_BINDING: static_assert(GL_DRAW_FRAMEBUFFER_BINDING == GL_DRAW_FRAMEBUFFER_BINDING_ANGLE, "Enum mismatch"); *params = mDrawFramebuffer->id().value; break; case GL_READ_FRAMEBUFFER_BINDING: static_assert(GL_READ_FRAMEBUFFER_BINDING == GL_READ_FRAMEBUFFER_BINDING_ANGLE, "Enum mismatch"); *params = mReadFramebuffer->id().value; break; case GL_RENDERBUFFER_BINDING: *params = mRenderbuffer.id().value; break; case GL_VERTEX_ARRAY_BINDING: *params = mVertexArray->id().value; break; case GL_CURRENT_PROGRAM: *params = mProgram ? mProgram->id().value : 0; break; case GL_PACK_ALIGNMENT: *params = mPack.alignment; break; case GL_PACK_REVERSE_ROW_ORDER_ANGLE: *params = mPack.reverseRowOrder; break; case GL_PACK_ROW_LENGTH: *params = mPack.rowLength; break; case GL_PACK_SKIP_ROWS: *params = mPack.skipRows; break; case GL_PACK_SKIP_PIXELS: *params = mPack.skipPixels; break; case GL_UNPACK_ALIGNMENT: *params = mUnpack.alignment; break; case GL_UNPACK_ROW_LENGTH: *params = mUnpack.rowLength; break; case GL_UNPACK_IMAGE_HEIGHT: *params = mUnpack.imageHeight; break; case GL_UNPACK_SKIP_IMAGES: *params = mUnpack.skipImages; break; case GL_UNPACK_SKIP_ROWS: *params = mUnpack.skipRows; break; case GL_UNPACK_SKIP_PIXELS: *params = mUnpack.skipPixels; break; case GL_GENERATE_MIPMAP_HINT: *params = mGenerateMipmapHint; break; case GL_TEXTURE_FILTERING_HINT_CHROMIUM: *params = mTextureFilteringHint; break; case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES: *params = mFragmentShaderDerivativeHint; break; case GL_ACTIVE_TEXTURE: *params = (static_cast(mActiveSampler) + GL_TEXTURE0); break; case GL_STENCIL_FUNC: *params = mDepthStencil.stencilFunc; break; case GL_STENCIL_REF: *params = mStencilRef; break; case GL_STENCIL_VALUE_MASK: *params = CastMaskValue(mDepthStencil.stencilMask); break; case GL_STENCIL_BACK_FUNC: *params = mDepthStencil.stencilBackFunc; break; case GL_STENCIL_BACK_REF: *params = mStencilBackRef; break; case GL_STENCIL_BACK_VALUE_MASK: *params = CastMaskValue(mDepthStencil.stencilBackMask); break; case GL_STENCIL_FAIL: *params = mDepthStencil.stencilFail; break; case GL_STENCIL_PASS_DEPTH_FAIL: *params = mDepthStencil.stencilPassDepthFail; break; case GL_STENCIL_PASS_DEPTH_PASS: *params = mDepthStencil.stencilPassDepthPass; break; case GL_STENCIL_BACK_FAIL: *params = mDepthStencil.stencilBackFail; break; case GL_STENCIL_BACK_PASS_DEPTH_FAIL: *params = mDepthStencil.stencilBackPassDepthFail; break; case GL_STENCIL_BACK_PASS_DEPTH_PASS: *params = mDepthStencil.stencilBackPassDepthPass; break; case GL_DEPTH_FUNC: *params = mDepthStencil.depthFunc; break; case GL_BLEND_SRC_RGB: // non-indexed get returns the state of draw buffer zero *params = mBlendStateExt.getSrcColorIndexed(0); break; case GL_BLEND_SRC_ALPHA: *params = mBlendStateExt.getSrcAlphaIndexed(0); break; case GL_BLEND_DST_RGB: *params = mBlendStateExt.getDstColorIndexed(0); break; case GL_BLEND_DST_ALPHA: *params = mBlendStateExt.getDstAlphaIndexed(0); break; case GL_BLEND_EQUATION_RGB: *params = mBlendStateExt.getEquationColorIndexed(0); break; case GL_BLEND_EQUATION_ALPHA: *params = mBlendStateExt.getEquationAlphaIndexed(0); break; case GL_STENCIL_WRITEMASK: *params = CastMaskValue(mDepthStencil.stencilWritemask); break; case GL_STENCIL_BACK_WRITEMASK: *params = CastMaskValue(mDepthStencil.stencilBackWritemask); break; case GL_STENCIL_CLEAR_VALUE: *params = mStencilClearValue; break; case GL_IMPLEMENTATION_COLOR_READ_TYPE: *params = mReadFramebuffer->getImplementationColorReadType(context); break; case GL_IMPLEMENTATION_COLOR_READ_FORMAT: *params = mReadFramebuffer->getImplementationColorReadFormat(context); break; case GL_SAMPLE_BUFFERS: case GL_SAMPLES: { Framebuffer *framebuffer = mDrawFramebuffer; if (framebuffer->isComplete(context)) { GLint samples = framebuffer->getSamples(context); switch (pname) { case GL_SAMPLE_BUFFERS: if (samples != 0) { *params = 1; } else { *params = 0; } break; case GL_SAMPLES: *params = samples; break; } } else { *params = 0; } } break; case GL_VIEWPORT: params[0] = mViewport.x; params[1] = mViewport.y; params[2] = mViewport.width; params[3] = mViewport.height; break; case GL_SCISSOR_BOX: params[0] = mScissor.x; params[1] = mScissor.y; params[2] = mScissor.width; params[3] = mScissor.height; break; case GL_CULL_FACE_MODE: *params = ToGLenum(mRasterizer.cullMode); break; case GL_FRONT_FACE: *params = mRasterizer.frontFace; break; case GL_RED_BITS: case GL_GREEN_BITS: case GL_BLUE_BITS: case GL_ALPHA_BITS: { Framebuffer *framebuffer = getDrawFramebuffer(); const FramebufferAttachment *colorbuffer = framebuffer->getFirstColorAttachment(); if (colorbuffer) { switch (pname) { case GL_RED_BITS: *params = colorbuffer->getRedSize(); break; case GL_GREEN_BITS: *params = colorbuffer->getGreenSize(); break; case GL_BLUE_BITS: *params = colorbuffer->getBlueSize(); break; case GL_ALPHA_BITS: *params = colorbuffer->getAlphaSize(); break; } } else { *params = 0; } } break; case GL_DEPTH_BITS: { const Framebuffer *framebuffer = getDrawFramebuffer(); const FramebufferAttachment *depthbuffer = framebuffer->getDepthAttachment(); if (depthbuffer) { *params = depthbuffer->getDepthSize(); } else { *params = 0; } } break; case GL_STENCIL_BITS: { const Framebuffer *framebuffer = getDrawFramebuffer(); const FramebufferAttachment *stencilbuffer = framebuffer->getStencilAttachment(); if (stencilbuffer) { *params = stencilbuffer->getStencilSize(); } else { *params = 0; } } break; case GL_TEXTURE_BINDING_2D: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::_2D) .value; break; case GL_TEXTURE_BINDING_RECTANGLE_ANGLE: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::Rectangle) .value; break; case GL_TEXTURE_BINDING_CUBE_MAP: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::CubeMap) .value; break; case GL_TEXTURE_BINDING_3D: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::_3D) .value; break; case GL_TEXTURE_BINDING_2D_ARRAY: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::_2DArray) .value; break; case GL_TEXTURE_BINDING_2D_MULTISAMPLE: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::_2DMultisample) .value; break; case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::_2DMultisampleArray) .value; break; case GL_TEXTURE_BINDING_CUBE_MAP_ARRAY: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::CubeMapArray) .value; break; case GL_TEXTURE_BINDING_EXTERNAL_OES: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::External) .value; break; // GL_OES_texture_buffer case GL_TEXTURE_BINDING_BUFFER: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerTextureId(static_cast(mActiveSampler), TextureType::Buffer) .value; break; case GL_TEXTURE_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::Texture].id().value; break; case GL_UNIFORM_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::Uniform].id().value; break; case GL_TRANSFORM_FEEDBACK_BINDING: *params = mTransformFeedback.id().value; break; case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::TransformFeedback].id().value; break; case GL_COPY_READ_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::CopyRead].id().value; break; case GL_COPY_WRITE_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::CopyWrite].id().value; break; case GL_PIXEL_PACK_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::PixelPack].id().value; break; case GL_PIXEL_UNPACK_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::PixelUnpack].id().value; break; case GL_READ_BUFFER: *params = mReadFramebuffer->getReadBufferState(); break; case GL_SAMPLER_BINDING: ASSERT(mActiveSampler < mCaps.maxCombinedTextureImageUnits); *params = getSamplerId(static_cast(mActiveSampler)).value; break; case GL_DEBUG_LOGGED_MESSAGES: *params = static_cast(mDebug.getMessageCount()); break; case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH: *params = static_cast(mDebug.getNextMessageLength()); break; case GL_DEBUG_GROUP_STACK_DEPTH: *params = static_cast(mDebug.getGroupStackDepth()); break; case GL_MULTISAMPLE_EXT: *params = static_cast(mMultiSampling); break; case GL_SAMPLE_ALPHA_TO_ONE_EXT: *params = static_cast(mSampleAlphaToOne); break; case GL_COVERAGE_MODULATION_CHROMIUM: *params = static_cast(mCoverageModulation); break; case GL_ATOMIC_COUNTER_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::AtomicCounter].id().value; break; case GL_SHADER_STORAGE_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::ShaderStorage].id().value; break; case GL_DISPATCH_INDIRECT_BUFFER_BINDING: *params = mBoundBuffers[BufferBinding::DispatchIndirect].id().value; break; case GL_ALPHA_TEST_FUNC: *params = ToGLenum(mGLES1State.mAlphaTestFunc); break; case GL_CLIENT_ACTIVE_TEXTURE: *params = mGLES1State.mClientActiveTexture + GL_TEXTURE0; break; case GL_MATRIX_MODE: *params = ToGLenum(mGLES1State.mMatrixMode); break; case GL_SHADE_MODEL: *params = ToGLenum(mGLES1State.mShadeModel); break; case GL_MODELVIEW_STACK_DEPTH: case GL_PROJECTION_STACK_DEPTH: case GL_TEXTURE_STACK_DEPTH: *params = mGLES1State.getCurrentMatrixStackDepth(pname); break; case GL_LOGIC_OP_MODE: *params = ToGLenum(mGLES1State.mLogicOp); break; case GL_BLEND_SRC: // non-indexed get returns the state of draw buffer zero *params = mBlendStateExt.getSrcColorIndexed(0); break; case GL_BLEND_DST: *params = mBlendStateExt.getDstColorIndexed(0); break; case GL_PERSPECTIVE_CORRECTION_HINT: case GL_POINT_SMOOTH_HINT: case GL_LINE_SMOOTH_HINT: case GL_FOG_HINT: *params = mGLES1State.getHint(pname); break; // GL_ANGLE_provoking_vertex case GL_PROVOKING_VERTEX: *params = ToGLenum(mProvokingVertex); break; case GL_PROGRAM_PIPELINE_BINDING: { ProgramPipeline *pipeline = getProgramPipeline(); if (pipeline) { *params = pipeline->id().value; } else { *params = 0; } break; } case GL_PATCH_VERTICES: *params = mPatchVertices; break; // GL_EXT_clip_control case GL_CLIP_ORIGIN_EXT: *params = mClipControlOrigin; break; case GL_CLIP_DEPTH_MODE_EXT: *params = mClipControlDepth; break; // GL_QCOM_shading_rate case GL_SHADING_RATE_QCOM: *params = ToGLenum(mShadingRate); break; default: UNREACHABLE(); break; } return angle::Result::Continue; } void State::getPointerv(const Context *context, GLenum pname, void **params) const { switch (pname) { case GL_DEBUG_CALLBACK_FUNCTION: *params = reinterpret_cast(mDebug.getCallback()); break; case GL_DEBUG_CALLBACK_USER_PARAM: *params = const_cast(mDebug.getUserParam()); break; case GL_VERTEX_ARRAY_POINTER: case GL_NORMAL_ARRAY_POINTER: case GL_COLOR_ARRAY_POINTER: case GL_TEXTURE_COORD_ARRAY_POINTER: case GL_POINT_SIZE_ARRAY_POINTER_OES: QueryVertexAttribPointerv(getVertexArray()->getVertexAttribute( context->vertexArrayIndex(ParamToVertexArrayType(pname))), GL_VERTEX_ATTRIB_ARRAY_POINTER, params); return; default: UNREACHABLE(); break; } } void State::getIntegeri_v(const Context *context, GLenum target, GLuint index, GLint *data) const { switch (target) { case GL_BLEND_SRC_RGB: ASSERT(static_cast(index) < mBlendStateExt.getDrawBufferCount()); *data = mBlendStateExt.getSrcColorIndexed(index); break; case GL_BLEND_SRC_ALPHA: ASSERT(static_cast(index) < mBlendStateExt.getDrawBufferCount()); *data = mBlendStateExt.getSrcAlphaIndexed(index); break; case GL_BLEND_DST_RGB: ASSERT(static_cast(index) < mBlendStateExt.getDrawBufferCount()); *data = mBlendStateExt.getDstColorIndexed(index); break; case GL_BLEND_DST_ALPHA: ASSERT(static_cast(index) < mBlendStateExt.getDrawBufferCount()); *data = mBlendStateExt.getDstAlphaIndexed(index); break; case GL_BLEND_EQUATION_RGB: ASSERT(static_cast(index) < mBlendStateExt.getDrawBufferCount()); *data = mBlendStateExt.getEquationColorIndexed(index); break; case GL_BLEND_EQUATION_ALPHA: ASSERT(static_cast(index) < mBlendStateExt.getDrawBufferCount()); *data = mBlendStateExt.getEquationAlphaIndexed(index); break; case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: ASSERT(static_cast(index) < mTransformFeedback->getIndexedBufferCount()); *data = mTransformFeedback->getIndexedBuffer(index).id().value; break; case GL_UNIFORM_BUFFER_BINDING: ASSERT(static_cast(index) < mUniformBuffers.size()); *data = mUniformBuffers[index].id().value; break; case GL_ATOMIC_COUNTER_BUFFER_BINDING: ASSERT(static_cast(index) < mAtomicCounterBuffers.size()); *data = mAtomicCounterBuffers[index].id().value; break; case GL_SHADER_STORAGE_BUFFER_BINDING: ASSERT(static_cast(index) < mShaderStorageBuffers.size()); *data = mShaderStorageBuffers[index].id().value; break; case GL_VERTEX_BINDING_BUFFER: ASSERT(static_cast(index) < mVertexArray->getMaxBindings()); *data = mVertexArray->getVertexBinding(index).getBuffer().id().value; break; case GL_VERTEX_BINDING_DIVISOR: ASSERT(static_cast(index) < mVertexArray->getMaxBindings()); *data = mVertexArray->getVertexBinding(index).getDivisor(); break; case GL_VERTEX_BINDING_OFFSET: ASSERT(static_cast(index) < mVertexArray->getMaxBindings()); *data = static_cast(mVertexArray->getVertexBinding(index).getOffset()); break; case GL_VERTEX_BINDING_STRIDE: ASSERT(static_cast(index) < mVertexArray->getMaxBindings()); *data = mVertexArray->getVertexBinding(index).getStride(); break; case GL_SAMPLE_MASK_VALUE: ASSERT(static_cast(index) < mSampleMaskValues.size()); *data = mSampleMaskValues[index]; break; case GL_IMAGE_BINDING_NAME: ASSERT(static_cast(index) < mImageUnits.size()); *data = mImageUnits[index].texture.id().value; break; case GL_IMAGE_BINDING_LEVEL: ASSERT(static_cast(index) < mImageUnits.size()); *data = mImageUnits[index].level; break; case GL_IMAGE_BINDING_LAYER: ASSERT(static_cast(index) < mImageUnits.size()); *data = mImageUnits[index].layer; break; case GL_IMAGE_BINDING_ACCESS: ASSERT(static_cast(index) < mImageUnits.size()); *data = mImageUnits[index].access; break; case GL_IMAGE_BINDING_FORMAT: ASSERT(static_cast(index) < mImageUnits.size()); *data = mImageUnits[index].format; break; // GL_ANGLE_shader_pixel_local_storage. case GL_PIXEL_LOCAL_FORMAT_ANGLE: case GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE: case GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE: case GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE: { ASSERT(mDrawFramebuffer); *data = mDrawFramebuffer->getPixelLocalStorage(context).getPlane(index).getIntegeri( context, target, index); break; } default: UNREACHABLE(); break; } } void State::getInteger64i_v(GLenum target, GLuint index, GLint64 *data) const { switch (target) { case GL_TRANSFORM_FEEDBACK_BUFFER_START: ASSERT(static_cast(index) < mTransformFeedback->getIndexedBufferCount()); *data = mTransformFeedback->getIndexedBuffer(index).getOffset(); break; case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: ASSERT(static_cast(index) < mTransformFeedback->getIndexedBufferCount()); *data = mTransformFeedback->getIndexedBuffer(index).getSize(); break; case GL_UNIFORM_BUFFER_START: ASSERT(static_cast(index) < mUniformBuffers.size()); *data = mUniformBuffers[index].getOffset(); break; case GL_UNIFORM_BUFFER_SIZE: ASSERT(static_cast(index) < mUniformBuffers.size()); *data = mUniformBuffers[index].getSize(); break; case GL_ATOMIC_COUNTER_BUFFER_START: ASSERT(static_cast(index) < mAtomicCounterBuffers.size()); *data = mAtomicCounterBuffers[index].getOffset(); break; case GL_ATOMIC_COUNTER_BUFFER_SIZE: ASSERT(static_cast(index) < mAtomicCounterBuffers.size()); *data = mAtomicCounterBuffers[index].getSize(); break; case GL_SHADER_STORAGE_BUFFER_START: ASSERT(static_cast(index) < mShaderStorageBuffers.size()); *data = mShaderStorageBuffers[index].getOffset(); break; case GL_SHADER_STORAGE_BUFFER_SIZE: ASSERT(static_cast(index) < mShaderStorageBuffers.size()); *data = mShaderStorageBuffers[index].getSize(); break; default: UNREACHABLE(); break; } } void State::getBooleani_v(GLenum target, GLuint index, GLboolean *data) const { switch (target) { case GL_COLOR_WRITEMASK: { ASSERT(static_cast(index) < mBlendStateExt.getDrawBufferCount()); bool r, g, b, a; mBlendStateExt.getColorMaskIndexed(index, &r, &g, &b, &a); data[0] = r; data[1] = g; data[2] = b; data[3] = a; break; } case GL_IMAGE_BINDING_LAYERED: ASSERT(static_cast(index) < mImageUnits.size()); *data = mImageUnits[index].layered; break; default: UNREACHABLE(); break; } } // TODO(http://anglebug.com/3889): Remove this helper function after blink and chromium part // refactor done. Texture *State::getTextureForActiveSampler(TextureType type, size_t index) { if (type != TextureType::VideoImage) { return mSamplerTextures[type][index].get(); } ASSERT(type == TextureType::VideoImage); Texture *candidateTexture = mSamplerTextures[type][index].get(); if (candidateTexture->getWidth(TextureTarget::VideoImage, 0) == 0 || candidateTexture->getHeight(TextureTarget::VideoImage, 0) == 0 || candidateTexture->getDepth(TextureTarget::VideoImage, 0) == 0) { return mSamplerTextures[TextureType::_2D][index].get(); } return mSamplerTextures[type][index].get(); } angle::Result State::syncActiveTextures(const Context *context, Command command) { if (mDirtyActiveTextures.none()) { return angle::Result::Continue; } for (size_t textureUnit : mDirtyActiveTextures) { if (mExecutable) { TextureType type = mExecutable->getActiveSamplerTypes()[textureUnit]; Texture *activeTexture = (type != TextureType::InvalidEnum) ? getTextureForActiveSampler(type, textureUnit) : nullptr; const Sampler *sampler = mSamplers[textureUnit].get(); updateActiveTextureStateOnSync(context, textureUnit, sampler, activeTexture); } } mDirtyActiveTextures.reset(); return angle::Result::Continue; } angle::Result State::syncTexturesInit(const Context *context, Command command) { ASSERT(mRobustResourceInit); if (!mProgram) return angle::Result::Continue; for (size_t textureUnitIndex : mExecutable->getActiveSamplersMask()) { Texture *texture = mActiveTexturesCache[textureUnitIndex]; if (texture) { ANGLE_TRY(texture->ensureInitialized(context)); } } return angle::Result::Continue; } angle::Result State::syncImagesInit(const Context *context, Command command) { ASSERT(mRobustResourceInit); ASSERT(mExecutable); for (size_t imageUnitIndex : mExecutable->getActiveImagesMask()) { Texture *texture = mImageUnits[imageUnitIndex].texture.get(); if (texture) { ANGLE_TRY(texture->ensureInitialized(context)); } } return angle::Result::Continue; } angle::Result State::syncReadAttachments(const Context *context, Command command) { ASSERT(mReadFramebuffer); ASSERT(mRobustResourceInit); return mReadFramebuffer->ensureReadAttachmentsInitialized(context); } angle::Result State::syncDrawAttachments(const Context *context, Command command) { ASSERT(mDrawFramebuffer); ASSERT(mRobustResourceInit); return mDrawFramebuffer->ensureDrawAttachmentsInitialized(context); } angle::Result State::syncReadFramebuffer(const Context *context, Command command) { ASSERT(mReadFramebuffer); return mReadFramebuffer->syncState(context, GL_READ_FRAMEBUFFER, command); } angle::Result State::syncDrawFramebuffer(const Context *context, Command command) { ASSERT(mDrawFramebuffer); mDrawFramebuffer->setWriteControlMode(context->getState().getFramebufferSRGB() ? SrgbWriteControlMode::Default : SrgbWriteControlMode::Linear); return mDrawFramebuffer->syncState(context, GL_DRAW_FRAMEBUFFER, command); } angle::Result State::syncTextures(const Context *context, Command command) { if (mDirtyTextures.none()) return angle::Result::Continue; for (size_t textureIndex : mDirtyTextures) { Texture *texture = mActiveTexturesCache[textureIndex]; if (texture && texture->hasAnyDirtyBit()) { ANGLE_TRY(texture->syncState(context, Command::Other)); } } mDirtyTextures.reset(); return angle::Result::Continue; } angle::Result State::syncImages(const Context *context, Command command) { if (mDirtyImages.none()) return angle::Result::Continue; for (size_t imageUnitIndex : mDirtyImages) { Texture *texture = mImageUnits[imageUnitIndex].texture.get(); if (texture && texture->hasAnyDirtyBit()) { ANGLE_TRY(texture->syncState(context, Command::Other)); } } mDirtyImages.reset(); return angle::Result::Continue; } angle::Result State::syncSamplers(const Context *context, Command command) { if (mDirtySamplers.none()) return angle::Result::Continue; for (size_t samplerIndex : mDirtySamplers) { BindingPointer &sampler = mSamplers[samplerIndex]; if (sampler.get() && sampler->isDirty()) { ANGLE_TRY(sampler->syncState(context)); } } mDirtySamplers.reset(); return angle::Result::Continue; } angle::Result State::syncVertexArray(const Context *context, Command command) { ASSERT(mVertexArray); return mVertexArray->syncState(context); } angle::Result State::syncProgram(const Context *context, Command command) { // There may not be a program if the calling application only uses program pipelines. if (mProgram) { return mProgram->syncState(context); } return angle::Result::Continue; } angle::Result State::syncProgramPipelineObject(const Context *context, Command command) { // If a ProgramPipeline is bound, ensure it is linked. if (mProgramPipeline.get()) { mProgramPipeline->resolveLink(context); } return angle::Result::Continue; } angle::Result State::syncDirtyObject(const Context *context, GLenum target) { DirtyObjects localSet; switch (target) { case GL_READ_FRAMEBUFFER: localSet.set(DIRTY_OBJECT_READ_FRAMEBUFFER); break; case GL_DRAW_FRAMEBUFFER: localSet.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); break; case GL_FRAMEBUFFER: localSet.set(DIRTY_OBJECT_READ_FRAMEBUFFER); localSet.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); break; case GL_VERTEX_ARRAY: localSet.set(DIRTY_OBJECT_VERTEX_ARRAY); break; case GL_TEXTURE: localSet.set(DIRTY_OBJECT_TEXTURES); break; case GL_SAMPLER: localSet.set(DIRTY_OBJECT_SAMPLERS); break; case GL_PROGRAM: localSet.set(DIRTY_OBJECT_PROGRAM); break; } return syncDirtyObjects(context, localSet, Command::Other); } void State::setObjectDirty(GLenum target) { switch (target) { case GL_READ_FRAMEBUFFER: mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); break; case GL_DRAW_FRAMEBUFFER: setDrawFramebufferDirty(); break; case GL_FRAMEBUFFER: mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); setDrawFramebufferDirty(); break; case GL_VERTEX_ARRAY: mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); break; case GL_PROGRAM: mDirtyObjects.set(DIRTY_OBJECT_PROGRAM); break; default: break; } } angle::Result State::onProgramExecutableChange(const Context *context, Program *program) { // OpenGL Spec: // "If LinkProgram or ProgramBinary successfully re-links a program object // that was already in use as a result of a previous call to UseProgram, then the // generated executable code will be installed as part of the current rendering state." ASSERT(program->isLinked()); // If this Program is currently active, we need to update the State's pointer to the current // ProgramExecutable if we just changed it. if (mProgram == program) { mExecutable = &program->getExecutable(); } mDirtyBits.set(DIRTY_BIT_PROGRAM_EXECUTABLE); if (program->hasAnyDirtyBit()) { mDirtyObjects.set(DIRTY_OBJECT_PROGRAM); } // Set any bound textures. const ProgramExecutable &executable = program->getExecutable(); const ActiveTextureTypeArray &textureTypes = executable.getActiveSamplerTypes(); for (size_t textureIndex : executable.getActiveSamplersMask()) { TextureType type = textureTypes[textureIndex]; // This can happen if there is a conflicting texture type. if (type == TextureType::InvalidEnum) continue; Texture *texture = getTextureForActiveSampler(type, textureIndex); updateTextureBinding(context, textureIndex, texture); } for (size_t imageUnitIndex : executable.getActiveImagesMask()) { Texture *image = mImageUnits[imageUnitIndex].texture.get(); if (!image) continue; if (image->hasAnyDirtyBit()) { ANGLE_TRY(image->syncState(context, Command::Other)); } if (mRobustResourceInit && image->initState() == InitState::MayNeedInit) { mDirtyObjects.set(DIRTY_OBJECT_IMAGES_INIT); } } return angle::Result::Continue; } angle::Result State::onProgramPipelineExecutableChange(const Context *context) { mDirtyBits.set(DIRTY_BIT_PROGRAM_EXECUTABLE); if (!mProgramPipeline->isLinked()) { mDirtyObjects.set(DIRTY_OBJECT_PROGRAM_PIPELINE_OBJECT); } // Set any bound textures. const ProgramExecutable &executable = mProgramPipeline->getExecutable(); const ActiveTextureTypeArray &textureTypes = executable.getActiveSamplerTypes(); for (size_t textureIndex : executable.getActiveSamplersMask()) { TextureType type = textureTypes[textureIndex]; // This can happen if there is a conflicting texture type. if (type == TextureType::InvalidEnum) continue; Texture *texture = getTextureForActiveSampler(type, textureIndex); updateTextureBinding(context, textureIndex, texture); } for (size_t imageUnitIndex : executable.getActiveImagesMask()) { Texture *image = mImageUnits[imageUnitIndex].texture.get(); if (!image) continue; if (image->hasAnyDirtyBit()) { ANGLE_TRY(image->syncState(context, Command::Other)); } if (mRobustResourceInit && image->initState() == InitState::MayNeedInit) { mDirtyObjects.set(DIRTY_OBJECT_IMAGES_INIT); } } return angle::Result::Continue; } void State::setTextureDirty(size_t textureUnitIndex) { mDirtyObjects.set(DIRTY_OBJECT_TEXTURES); mDirtyTextures.set(textureUnitIndex); } void State::setSamplerDirty(size_t samplerIndex) { mDirtyObjects.set(DIRTY_OBJECT_SAMPLERS); mDirtySamplers.set(samplerIndex); } void State::setImageUnit(const Context *context, size_t unit, Texture *texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { ASSERT(!mImageUnits.empty()); ImageUnit &imageUnit = mImageUnits[unit]; if (texture) { texture->onBindAsImageTexture(); // Using individual layers of a 3d image as 2d may require that the image be respecified in // a compatible layout if (!layered && texture->getType() == TextureType::_3D) { texture->onBind3DTextureAs2DImage(); } } imageUnit.texture.set(context, texture); imageUnit.level = level; imageUnit.layered = layered; imageUnit.layer = layer; imageUnit.access = access; imageUnit.format = format; mDirtyBits.set(DIRTY_BIT_IMAGE_BINDINGS); onImageStateChange(context, unit); } // Handle a dirty texture event. void State::onActiveTextureChange(const Context *context, size_t textureUnit) { if (mExecutable) { TextureType type = mExecutable->getActiveSamplerTypes()[textureUnit]; Texture *activeTexture = (type != TextureType::InvalidEnum) ? getTextureForActiveSampler(type, textureUnit) : nullptr; updateTextureBinding(context, textureUnit, activeTexture); mExecutable->onStateChange(angle::SubjectMessage::ProgramTextureOrImageBindingChanged); } } void State::onActiveTextureStateChange(const Context *context, size_t textureUnit) { if (mExecutable) { TextureType type = mExecutable->getActiveSamplerTypes()[textureUnit]; Texture *activeTexture = (type != TextureType::InvalidEnum) ? getTextureForActiveSampler(type, textureUnit) : nullptr; setActiveTextureDirty(textureUnit, activeTexture); } } void State::onImageStateChange(const Context *context, size_t unit) { if (mExecutable) { const ImageUnit &image = mImageUnits[unit]; // Have nothing to do here if no texture bound if (!image.texture.get()) return; if (image.texture->hasAnyDirtyBit()) { mDirtyImages.set(unit); mDirtyObjects.set(DIRTY_OBJECT_IMAGES); } if (mRobustResourceInit && image.texture->initState() == InitState::MayNeedInit) { mDirtyObjects.set(DIRTY_OBJECT_IMAGES_INIT); } mExecutable->onStateChange(angle::SubjectMessage::ProgramTextureOrImageBindingChanged); } } void State::onUniformBufferStateChange(size_t uniformBufferIndex) { // This could be represented by a different dirty bit. Using the same one keeps it simple. mDirtyBits.set(DIRTY_BIT_UNIFORM_BUFFER_BINDINGS); } void State::onAtomicCounterBufferStateChange(size_t atomicCounterBufferIndex) { mDirtyBits.set(DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING); } void State::onShaderStorageBufferStateChange(size_t shaderStorageBufferIndex) { mDirtyBits.set(DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING); } AttributesMask State::getAndResetDirtyCurrentValues() const { AttributesMask retVal = mDirtyCurrentValues; mDirtyCurrentValues.reset(); return retVal; } State::ExtendedDirtyBits State::getAndResetExtendedDirtyBits() const { ExtendedDirtyBits retVal = mExtendedDirtyBits; mExtendedDirtyBits.reset(); return retVal; } void State::initializeForCapture(const Context *context) { mCaps = context->getCaps(); mExtensions = context->getExtensions(); // This little kludge gets around the frame capture "constness". It should be safe because // nothing in the context is modified in a non-compatible way during capture. Context *mutableContext = const_cast(context); initialize(mutableContext); } void State::setLogicOpEnabled(bool enabled) { if (mLogicOpEnabled != enabled) { mLogicOpEnabled = enabled; mDirtyBits.set(DIRTY_BIT_EXTENDED); mExtendedDirtyBits.set(EXTENDED_DIRTY_BIT_LOGIC_OP_ENABLED); } } void State::setLogicOp(LogicalOperation opcode) { if (mLogicOp != opcode) { mLogicOp = opcode; mDirtyBits.set(DIRTY_BIT_EXTENDED); mExtendedDirtyBits.set(EXTENDED_DIRTY_BIT_LOGIC_OP); } } constexpr State::DirtyObjectHandler State::kDirtyObjectHandlers[DIRTY_OBJECT_MAX]; } // namespace gl