// // Copyright 2015 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. // // StateManagerGL.h: Defines a class for caching applied OpenGL state #include "libANGLE/renderer/gl/StateManagerGL.h" #include #include #include #include "anglebase/numerics/safe_conversions.h" #include "common/bitset_utils.h" #include "common/mathutil.h" #include "common/matrix_utils.h" #include "libANGLE/Context.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/Query.h" #include "libANGLE/TransformFeedback.h" #include "libANGLE/VertexArray.h" #include "libANGLE/renderer/gl/BufferGL.h" #include "libANGLE/renderer/gl/FramebufferGL.h" #include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/ProgramGL.h" #include "libANGLE/renderer/gl/QueryGL.h" #include "libANGLE/renderer/gl/SamplerGL.h" #include "libANGLE/renderer/gl/TextureGL.h" #include "libANGLE/renderer/gl/TransformFeedbackGL.h" #include "libANGLE/renderer/gl/VertexArrayGL.h" namespace rx { namespace { static void ValidateStateHelper(const FunctionsGL *functions, const GLuint localValue, const GLenum pname, const char *localName, const char *driverName) { GLint queryValue; functions->getIntegerv(pname, &queryValue); if (localValue != static_cast(queryValue)) { WARN() << localName << " (" << localValue << ") != " << driverName << " (" << queryValue << ")"; // Re-add ASSERT: http://anglebug.com/3900 // ASSERT(false); } } } // anonymous namespace VertexArrayStateGL::VertexArrayStateGL(size_t maxAttribs, size_t maxBindings) : attributes(std::min(maxAttribs, gl::MAX_VERTEX_ATTRIBS)), bindings(std::min(maxBindings, gl::MAX_VERTEX_ATTRIBS)) { // Set the cached vertex attribute array and vertex attribute binding array size for (GLuint i = 0; i < attributes.size(); i++) { attributes[i].bindingIndex = i; } } StateManagerGL::IndexedBufferBinding::IndexedBufferBinding() : offset(0), size(0), buffer(0) {} StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &rendererCaps, const gl::Extensions &extensions, const angle::FeaturesGL &features) : mFunctions(functions), mFeatures(features), mProgram(0), mVAO(0), mVertexAttribCurrentValues(rendererCaps.maxVertexAttributes), mDefaultVAOState(rendererCaps.maxVertexAttributes, rendererCaps.maxVertexAttribBindings), mVAOState(&mDefaultVAOState), mBuffers(), mIndexedBuffers(), mTextureUnitIndex(0), mTextures{}, mSamplers{}, mImages(rendererCaps.maxImageUnits, ImageUnitBinding()), mTransformFeedback(0), mCurrentTransformFeedback(nullptr), mQueries(), mPrevDrawContext({0}), mUnpackAlignment(4), mUnpackRowLength(0), mUnpackSkipRows(0), mUnpackSkipPixels(0), mUnpackImageHeight(0), mUnpackSkipImages(0), mPackAlignment(4), mPackRowLength(0), mPackSkipRows(0), mPackSkipPixels(0), mFramebuffers(angle::FramebufferBindingSingletonMax, 0), mRenderbuffer(0), mScissorTestEnabled(false), mScissor(0, 0, 0, 0), mViewport(0, 0, 0, 0), mNear(0.0f), mFar(1.0f), mBlendColor(0, 0, 0, 0), mBlendStateExt(rendererCaps.maxDrawBuffers), mIndependentBlendStates(extensions.drawBuffersIndexedAny()), mSampleAlphaToCoverageEnabled(false), mSampleCoverageEnabled(false), mSampleCoverageValue(1.0f), mSampleCoverageInvert(false), mSampleMaskEnabled(false), mDepthTestEnabled(false), mDepthFunc(GL_LESS), mDepthMask(true), mStencilTestEnabled(false), mStencilFrontFunc(GL_ALWAYS), mStencilFrontRef(0), mStencilFrontValueMask(static_cast(-1)), mStencilFrontStencilFailOp(GL_KEEP), mStencilFrontStencilPassDepthFailOp(GL_KEEP), mStencilFrontStencilPassDepthPassOp(GL_KEEP), mStencilFrontWritemask(static_cast(-1)), mStencilBackFunc(GL_ALWAYS), mStencilBackRef(0), mStencilBackValueMask(static_cast(-1)), mStencilBackStencilFailOp(GL_KEEP), mStencilBackStencilPassDepthFailOp(GL_KEEP), mStencilBackStencilPassDepthPassOp(GL_KEEP), mStencilBackWritemask(static_cast(-1)), mCullFaceEnabled(false), mCullFace(gl::CullFaceMode::Back), mFrontFace(GL_CCW), mPolygonOffsetFillEnabled(false), mPolygonOffsetFactor(0.0f), mPolygonOffsetUnits(0.0f), mRasterizerDiscardEnabled(false), mLineWidth(1.0f), mPrimitiveRestartEnabled(false), mPrimitiveRestartIndex(0), mClearColor(0.0f, 0.0f, 0.0f, 0.0f), mClearDepth(1.0f), mClearStencil(0), mFramebufferSRGBAvailable(extensions.sRGBWriteControl), mFramebufferSRGBEnabled(false), mHasSeparateFramebufferBindings(mFunctions->isAtLeastGL(gl::Version(3, 0)) || mFunctions->isAtLeastGLES(gl::Version(3, 0))), mDitherEnabled(true), mTextureCubemapSeamlessEnabled(false), mMultisamplingEnabled(true), mSampleAlphaToOneEnabled(false), mCoverageModulation(GL_NONE), mIsMultiviewEnabled(extensions.multiview || extensions.multiview2), mProvokingVertex(GL_LAST_VERTEX_CONVENTION), mMaxClipDistances(rendererCaps.maxClipDistances), mLocalDirtyBits() { ASSERT(mFunctions); ASSERT(extensions.maxViews >= 1u); mIndexedBuffers[gl::BufferBinding::Uniform].resize(rendererCaps.maxUniformBufferBindings); mIndexedBuffers[gl::BufferBinding::AtomicCounter].resize( rendererCaps.maxAtomicCounterBufferBindings); mIndexedBuffers[gl::BufferBinding::ShaderStorage].resize( rendererCaps.maxShaderStorageBufferBindings); mSampleMaskValues.fill(~GLbitfield(0)); mQueries.fill(nullptr); mTemporaryPausedQueries.fill(nullptr); // Initialize point sprite state for desktop GL if (mFunctions->standard == STANDARD_GL_DESKTOP) { mFunctions->enable(GL_PROGRAM_POINT_SIZE); // GL_POINT_SPRITE was deprecated in the core profile. Point rasterization is always // performed // as though POINT_SPRITE were enabled. if ((mFunctions->profile & GL_CONTEXT_CORE_PROFILE_BIT) == 0) { mFunctions->enable(GL_POINT_SPRITE); } } if (features.emulatePrimitiveRestartFixedIndex.enabled) { // There is no consistent default value for primitive restart index. Set it to UINT -1. constexpr GLuint primitiveRestartIndex = gl::GetPrimitiveRestartIndexFromType(); mFunctions->primitiveRestartIndex(primitiveRestartIndex); mPrimitiveRestartIndex = primitiveRestartIndex; } // It's possible we've enabled the emulated VAO feature for testing but we're on a core profile. // Use a generated VAO as the default VAO so we can still test. if (features.syncVertexArraysToDefault.enabled && !nativegl::CanUseDefaultVertexArrayObject(mFunctions)) { ASSERT(nativegl::SupportsVertexArrayObjects(mFunctions)); mFunctions->genVertexArrays(1, &mDefaultVAO); mFunctions->bindVertexArray(mDefaultVAO); mVAO = mDefaultVAO; } } StateManagerGL::~StateManagerGL() { if (mDefaultVAO != 0) { mFunctions->deleteVertexArrays(1, &mDefaultVAO); } } void StateManagerGL::deleteProgram(GLuint program) { if (program != 0) { if (mProgram == program) { useProgram(0); } mFunctions->deleteProgram(program); } } void StateManagerGL::deleteVertexArray(GLuint vao) { if (vao != 0) { if (mVAO == vao) { bindVertexArray(0, &mDefaultVAOState); } mFunctions->deleteVertexArrays(1, &vao); } } void StateManagerGL::deleteTexture(GLuint texture) { if (texture != 0) { for (gl::TextureType type : angle::AllEnums()) { const auto &textureVector = mTextures[type]; for (size_t textureUnitIndex = 0; textureUnitIndex < textureVector.size(); textureUnitIndex++) { if (textureVector[textureUnitIndex] == texture) { activeTexture(textureUnitIndex); bindTexture(type, 0); } } } for (size_t imageUnitIndex = 0; imageUnitIndex < mImages.size(); imageUnitIndex++) { if (mImages[imageUnitIndex].texture == texture) { bindImageTexture(imageUnitIndex, 0, 0, false, 0, GL_READ_ONLY, GL_R32UI); } } mFunctions->deleteTextures(1, &texture); } } void StateManagerGL::deleteSampler(GLuint sampler) { if (sampler != 0) { for (size_t unit = 0; unit < mSamplers.size(); unit++) { if (mSamplers[unit] == sampler) { bindSampler(unit, 0); } } mFunctions->deleteSamplers(1, &sampler); } } void StateManagerGL::deleteBuffer(GLuint buffer) { if (buffer == 0) { return; } for (auto target : angle::AllEnums()) { if (mBuffers[target] == buffer) { bindBuffer(target, 0); } auto &indexedTarget = mIndexedBuffers[target]; for (size_t bindIndex = 0; bindIndex < indexedTarget.size(); ++bindIndex) { if (indexedTarget[bindIndex].buffer == buffer) { bindBufferBase(target, bindIndex, 0); } } } if (mVAOState) { if (mVAOState->elementArrayBuffer == buffer) { mVAOState->elementArrayBuffer = 0; } for (VertexBindingGL &binding : mVAOState->bindings) { if (binding.buffer == buffer) { binding.buffer = 0; } } } mFunctions->deleteBuffers(1, &buffer); } void StateManagerGL::deleteFramebuffer(GLuint fbo) { if (fbo != 0) { if (mHasSeparateFramebufferBindings) { for (size_t binding = 0; binding < mFramebuffers.size(); ++binding) { if (mFramebuffers[binding] == fbo) { GLenum enumValue = angle::FramebufferBindingToEnum( static_cast(binding)); bindFramebuffer(enumValue, 0); } } } else { ASSERT(mFramebuffers[angle::FramebufferBindingRead] == mFramebuffers[angle::FramebufferBindingDraw]); if (mFramebuffers[angle::FramebufferBindingRead] == fbo) { bindFramebuffer(GL_FRAMEBUFFER, 0); } } mFunctions->deleteFramebuffers(1, &fbo); } } void StateManagerGL::deleteRenderbuffer(GLuint rbo) { if (rbo != 0) { if (mRenderbuffer == rbo) { bindRenderbuffer(GL_RENDERBUFFER, 0); } mFunctions->deleteRenderbuffers(1, &rbo); } } void StateManagerGL::deleteTransformFeedback(GLuint transformFeedback) { if (transformFeedback != 0) { if (mTransformFeedback == transformFeedback) { bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); } if (mCurrentTransformFeedback != nullptr && mCurrentTransformFeedback->getTransformFeedbackID() == transformFeedback) { mCurrentTransformFeedback = nullptr; } mFunctions->deleteTransformFeedbacks(1, &transformFeedback); } } void StateManagerGL::useProgram(GLuint program) { if (mProgram != program) { forceUseProgram(program); } } void StateManagerGL::forceUseProgram(GLuint program) { mProgram = program; mFunctions->useProgram(mProgram); mLocalDirtyBits.set(gl::State::DIRTY_BIT_PROGRAM_BINDING); } void StateManagerGL::bindVertexArray(GLuint vao, VertexArrayStateGL *vaoState) { ASSERT(vaoState); if (mVAO != vao) { ASSERT(!mFeatures.syncVertexArraysToDefault.enabled); mVAO = vao; mVAOState = vaoState; mBuffers[gl::BufferBinding::ElementArray] = vaoState ? vaoState->elementArrayBuffer : 0; mFunctions->bindVertexArray(vao); mLocalDirtyBits.set(gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING); } } void StateManagerGL::bindBuffer(gl::BufferBinding target, GLuint buffer) { // GL drivers differ in whether the transform feedback bind point is modified when // glBindTransformFeedback is called. To avoid these behavior differences we shouldn't try to // use it. ASSERT(target != gl::BufferBinding::TransformFeedback); if (mBuffers[target] != buffer) { mBuffers[target] = buffer; mFunctions->bindBuffer(gl::ToGLenum(target), buffer); } } void StateManagerGL::bindBufferBase(gl::BufferBinding target, size_t index, GLuint buffer) { // Transform feedback buffer bindings are tracked in TransformFeedbackGL ASSERT(target != gl::BufferBinding::TransformFeedback); ASSERT(index < mIndexedBuffers[target].size()); auto &binding = mIndexedBuffers[target][index]; if (binding.buffer != buffer || binding.offset != static_cast(-1) || binding.size != static_cast(-1)) { binding.buffer = buffer; binding.offset = static_cast(-1); binding.size = static_cast(-1); mBuffers[target] = buffer; mFunctions->bindBufferBase(gl::ToGLenum(target), static_cast(index), buffer); } } void StateManagerGL::bindBufferRange(gl::BufferBinding target, size_t index, GLuint buffer, size_t offset, size_t size) { // Transform feedback buffer bindings are tracked in TransformFeedbackGL ASSERT(target != gl::BufferBinding::TransformFeedback); auto &binding = mIndexedBuffers[target][index]; if (binding.buffer != buffer || binding.offset != offset || binding.size != size) { binding.buffer = buffer; binding.offset = offset; binding.size = size; mBuffers[target] = buffer; mFunctions->bindBufferRange(gl::ToGLenum(target), static_cast(index), buffer, offset, size); } } void StateManagerGL::activeTexture(size_t unit) { if (mTextureUnitIndex != unit) { mTextureUnitIndex = unit; mFunctions->activeTexture(GL_TEXTURE0 + static_cast(mTextureUnitIndex)); } } void StateManagerGL::bindTexture(gl::TextureType type, GLuint texture) { gl::TextureType nativeType = nativegl::GetNativeTextureType(type); if (mTextures[nativeType][mTextureUnitIndex] != texture) { mTextures[nativeType][mTextureUnitIndex] = texture; mFunctions->bindTexture(nativegl::GetTextureBindingTarget(type), texture); mLocalDirtyBits.set(gl::State::DIRTY_BIT_TEXTURE_BINDINGS); } } void StateManagerGL::invalidateTexture(gl::TextureType type) { // Assume the tracked texture binding is incorrect, query the real bound texture from GL. GLint boundTexture = 0; mFunctions->getIntegerv(nativegl::GetTextureBindingQuery(type), &boundTexture); mTextures[type][mTextureUnitIndex] = static_cast(boundTexture); mLocalDirtyBits.set(gl::State::DIRTY_BIT_TEXTURE_BINDINGS); } void StateManagerGL::bindSampler(size_t unit, GLuint sampler) { if (mSamplers[unit] != sampler) { mSamplers[unit] = sampler; mFunctions->bindSampler(static_cast(unit), sampler); mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLER_BINDINGS); } } void StateManagerGL::bindImageTexture(size_t unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { auto &binding = mImages[unit]; if (binding.texture != texture || binding.level != level || binding.layered != layered || binding.layer != layer || binding.access != access || binding.format != format) { binding.texture = texture; binding.level = level; binding.layered = layered; binding.layer = layer; binding.access = access; binding.format = format; mFunctions->bindImageTexture(angle::base::checked_cast(unit), texture, level, layered, layer, access, format); } } angle::Result StateManagerGL::setPixelUnpackState(const gl::Context *context, const gl::PixelUnpackState &unpack) { if (mUnpackAlignment != unpack.alignment) { mUnpackAlignment = unpack.alignment; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_ALIGNMENT, mUnpackAlignment)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE); } if (mUnpackRowLength != unpack.rowLength) { mUnpackRowLength = unpack.rowLength; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_ROW_LENGTH, mUnpackRowLength)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE); } if (mUnpackSkipRows != unpack.skipRows) { mUnpackSkipRows = unpack.skipRows; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_SKIP_ROWS, mUnpackSkipRows)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE); } if (mUnpackSkipPixels != unpack.skipPixels) { mUnpackSkipPixels = unpack.skipPixels; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_SKIP_PIXELS, mUnpackSkipPixels)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE); } if (mUnpackImageHeight != unpack.imageHeight) { mUnpackImageHeight = unpack.imageHeight; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_IMAGE_HEIGHT, mUnpackImageHeight)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE); } if (mUnpackSkipImages != unpack.skipImages) { mUnpackSkipImages = unpack.skipImages; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_SKIP_IMAGES, mUnpackSkipImages)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE); } return angle::Result::Continue; } angle::Result StateManagerGL::setPixelUnpackBuffer(const gl::Context *context, const gl::Buffer *pixelBuffer) { GLuint bufferID = 0; if (pixelBuffer != nullptr) { bufferID = GetImplAs(pixelBuffer)->getBufferID(); } bindBuffer(gl::BufferBinding::PixelUnpack, bufferID); return angle::Result::Continue; } angle::Result StateManagerGL::setPixelPackState(const gl::Context *context, const gl::PixelPackState &pack) { if (mPackAlignment != pack.alignment) { mPackAlignment = pack.alignment; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_PACK_ALIGNMENT, mPackAlignment)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE); } if (mPackRowLength != pack.rowLength) { mPackRowLength = pack.rowLength; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_PACK_ROW_LENGTH, mPackRowLength)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE); } if (mPackSkipRows != pack.skipRows) { mPackSkipRows = pack.skipRows; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_PACK_SKIP_ROWS, mPackSkipRows)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE); } if (mPackSkipPixels != pack.skipPixels) { mPackSkipPixels = pack.skipPixels; ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_PACK_SKIP_PIXELS, mPackSkipPixels)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE); } return angle::Result::Continue; } angle::Result StateManagerGL::setPixelPackBuffer(const gl::Context *context, const gl::Buffer *pixelBuffer) { GLuint bufferID = 0; if (pixelBuffer != nullptr) { bufferID = GetImplAs(pixelBuffer)->getBufferID(); } bindBuffer(gl::BufferBinding::PixelPack, bufferID); return angle::Result::Continue; } void StateManagerGL::bindFramebuffer(GLenum type, GLuint framebuffer) { bool framebufferChanged = false; switch (type) { case GL_FRAMEBUFFER: if (mFramebuffers[angle::FramebufferBindingRead] != framebuffer || mFramebuffers[angle::FramebufferBindingDraw] != framebuffer) { mFramebuffers[angle::FramebufferBindingRead] = framebuffer; mFramebuffers[angle::FramebufferBindingDraw] = framebuffer; mFunctions->bindFramebuffer(GL_FRAMEBUFFER, framebuffer); mLocalDirtyBits.set(gl::State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); mLocalDirtyBits.set(gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); framebufferChanged = true; } break; case GL_READ_FRAMEBUFFER: ASSERT(mHasSeparateFramebufferBindings); if (mFramebuffers[angle::FramebufferBindingRead] != framebuffer) { mFramebuffers[angle::FramebufferBindingRead] = framebuffer; mFunctions->bindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); mLocalDirtyBits.set(gl::State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); framebufferChanged = true; } break; case GL_DRAW_FRAMEBUFFER: ASSERT(mHasSeparateFramebufferBindings); if (mFramebuffers[angle::FramebufferBindingDraw] != framebuffer) { mFramebuffers[angle::FramebufferBindingDraw] = framebuffer; mFunctions->bindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); mLocalDirtyBits.set(gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); framebufferChanged = true; } break; default: UNREACHABLE(); break; } if (framebufferChanged && mFeatures.flushOnFramebufferChange.enabled) { mFunctions->flush(); } } void StateManagerGL::bindRenderbuffer(GLenum type, GLuint renderbuffer) { ASSERT(type == GL_RENDERBUFFER); if (mRenderbuffer != renderbuffer) { mRenderbuffer = renderbuffer; mFunctions->bindRenderbuffer(type, mRenderbuffer); } } void StateManagerGL::bindTransformFeedback(GLenum type, GLuint transformFeedback) { ASSERT(type == GL_TRANSFORM_FEEDBACK); if (mTransformFeedback != transformFeedback) { // Pause the current transform feedback if one is active. // To handle virtualized contexts, StateManagerGL needs to be able to bind a new transform // feedback at any time, even if there is one active. if (mCurrentTransformFeedback != nullptr && mCurrentTransformFeedback->getTransformFeedbackID() != transformFeedback) { mCurrentTransformFeedback->syncPausedState(true); mCurrentTransformFeedback = nullptr; } mTransformFeedback = transformFeedback; mFunctions->bindTransformFeedback(type, mTransformFeedback); onTransformFeedbackStateChange(); } } void StateManagerGL::onTransformFeedbackStateChange() { mLocalDirtyBits.set(gl::State::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING); } void StateManagerGL::beginQuery(gl::QueryType type, QueryGL *queryObject, GLuint queryId) { // Make sure this is a valid query type and there is no current active query of this type ASSERT(mQueries[type] == nullptr); ASSERT(queryId != 0); mQueries[type] = queryObject; mFunctions->beginQuery(ToGLenum(type), queryId); } void StateManagerGL::endQuery(gl::QueryType type, QueryGL *queryObject, GLuint queryId) { ASSERT(queryObject != nullptr); ASSERT(mQueries[type] == queryObject); mQueries[type] = nullptr; mFunctions->endQuery(ToGLenum(type)); } void StateManagerGL::updateDrawIndirectBufferBinding(const gl::Context *context) { gl::Buffer *drawIndirectBuffer = context->getState().getTargetBuffer(gl::BufferBinding::DrawIndirect); if (drawIndirectBuffer != nullptr) { const BufferGL *bufferGL = GetImplAs(drawIndirectBuffer); bindBuffer(gl::BufferBinding::DrawIndirect, bufferGL->getBufferID()); } } void StateManagerGL::updateDispatchIndirectBufferBinding(const gl::Context *context) { gl::Buffer *dispatchIndirectBuffer = context->getState().getTargetBuffer(gl::BufferBinding::DispatchIndirect); if (dispatchIndirectBuffer != nullptr) { const BufferGL *bufferGL = GetImplAs(dispatchIndirectBuffer); bindBuffer(gl::BufferBinding::DispatchIndirect, bufferGL->getBufferID()); } } void StateManagerGL::pauseTransformFeedback() { if (mCurrentTransformFeedback != nullptr) { mCurrentTransformFeedback->syncPausedState(true); onTransformFeedbackStateChange(); } } angle::Result StateManagerGL::pauseAllQueries(const gl::Context *context) { for (gl::QueryType type : angle::AllEnums()) { QueryGL *previousQuery = mQueries[type]; if (previousQuery != nullptr) { ANGLE_TRY(previousQuery->pause(context)); mTemporaryPausedQueries[type] = previousQuery; mQueries[type] = nullptr; } } return angle::Result::Continue; } angle::Result StateManagerGL::pauseQuery(const gl::Context *context, gl::QueryType type) { QueryGL *previousQuery = mQueries[type]; if (previousQuery) { ANGLE_TRY(previousQuery->pause(context)); mTemporaryPausedQueries[type] = previousQuery; mQueries[type] = nullptr; } return angle::Result::Continue; } angle::Result StateManagerGL::resumeAllQueries(const gl::Context *context) { for (gl::QueryType type : angle::AllEnums()) { QueryGL *pausedQuery = mTemporaryPausedQueries[type]; if (pausedQuery != nullptr) { ASSERT(mQueries[type] == nullptr); ANGLE_TRY(pausedQuery->resume(context)); mTemporaryPausedQueries[type] = nullptr; } } return angle::Result::Continue; } angle::Result StateManagerGL::resumeQuery(const gl::Context *context, gl::QueryType type) { QueryGL *pausedQuery = mTemporaryPausedQueries[type]; if (pausedQuery != nullptr) { ANGLE_TRY(pausedQuery->resume(context)); mTemporaryPausedQueries[type] = nullptr; } return angle::Result::Continue; } angle::Result StateManagerGL::onMakeCurrent(const gl::Context *context) { const gl::State &glState = context->getState(); #if defined(ANGLE_ENABLE_ASSERTS) // Temporarily pausing queries during context switch is not supported for (QueryGL *pausedQuery : mTemporaryPausedQueries) { ASSERT(pausedQuery == nullptr); } #endif // If the context has changed, pause the previous context's queries auto contextID = context->getState().getContextID(); if (contextID != mPrevDrawContext) { for (gl::QueryType type : angle::AllEnums()) { QueryGL *currentQuery = mQueries[type]; // Pause any old query object if (currentQuery != nullptr) { ANGLE_TRY(currentQuery->pause(context)); mQueries[type] = nullptr; } // Check if this new context needs to resume a query gl::Query *newQuery = glState.getActiveQuery(type); if (newQuery != nullptr) { QueryGL *queryGL = GetImplAs(newQuery); ANGLE_TRY(queryGL->resume(context)); } } } onTransformFeedbackStateChange(); mPrevDrawContext = contextID; // Seamless cubemaps are required for ES3 and higher contexts. It should be the cheapest to set // this state here since MakeCurrent is expected to be called less frequently than draw calls. setTextureCubemapSeamlessEnabled(context->getClientMajorVersion() >= 3); return angle::Result::Continue; } void StateManagerGL::updateProgramTextureBindings(const gl::Context *context) { const gl::State &glState = context->getState(); const gl::ProgramExecutable *executable = glState.getProgramExecutable(); // It is possible there is no active program during a path operation. if (!executable) return; const gl::ActiveTexturesCache &textures = glState.getActiveTexturesCache(); const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask(); const gl::ActiveTextureTypeArray &textureTypes = executable->getActiveSamplerTypes(); for (size_t textureUnitIndex : activeTextures) { gl::TextureType textureType = textureTypes[textureUnitIndex]; gl::Texture *texture = textures[textureUnitIndex]; // A nullptr texture indicates incomplete. if (texture != nullptr) { const TextureGL *textureGL = GetImplAs(texture); ASSERT(!texture->hasAnyDirtyBit()); ASSERT(!textureGL->hasAnyDirtyBit()); activeTexture(textureUnitIndex); bindTexture(textureType, textureGL->getTextureID()); } else { activeTexture(textureUnitIndex); bindTexture(textureType, 0); } } } void StateManagerGL::updateProgramStorageBufferBindings(const gl::Context *context) { const gl::State &glState = context->getState(); const gl::Program *program = glState.getProgram(); for (size_t blockIndex = 0; blockIndex < program->getActiveShaderStorageBlockCount(); blockIndex++) { GLuint binding = program->getShaderStorageBlockBinding(static_cast(blockIndex)); const auto &shaderStorageBuffer = glState.getIndexedShaderStorageBuffer(binding); if (shaderStorageBuffer.get() != nullptr) { BufferGL *bufferGL = GetImplAs(shaderStorageBuffer.get()); if (shaderStorageBuffer.getSize() == 0) { bindBufferBase(gl::BufferBinding::ShaderStorage, binding, bufferGL->getBufferID()); } else { bindBufferRange(gl::BufferBinding::ShaderStorage, binding, bufferGL->getBufferID(), shaderStorageBuffer.getOffset(), shaderStorageBuffer.getSize()); } } } } void StateManagerGL::updateProgramUniformBufferBindings(const gl::Context *context) { // Sync the current program state const gl::State &glState = context->getState(); const gl::Program *program = glState.getProgram(); for (size_t uniformBlockIndex = 0; uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++) { GLuint binding = program->getUniformBlockBinding(static_cast(uniformBlockIndex)); const auto &uniformBuffer = glState.getIndexedUniformBuffer(binding); if (uniformBuffer.get() != nullptr) { BufferGL *bufferGL = GetImplAs(uniformBuffer.get()); if (uniformBuffer.getSize() == 0) { bindBufferBase(gl::BufferBinding::Uniform, binding, bufferGL->getBufferID()); } else { bindBufferRange(gl::BufferBinding::Uniform, binding, bufferGL->getBufferID(), uniformBuffer.getOffset(), uniformBuffer.getSize()); } } } } void StateManagerGL::updateProgramAtomicCounterBufferBindings(const gl::Context *context) { const gl::State &glState = context->getState(); const gl::Program *program = glState.getProgram(); for (const auto &atomicCounterBuffer : program->getState().getAtomicCounterBuffers()) { GLuint binding = atomicCounterBuffer.binding; const auto &buffer = glState.getIndexedAtomicCounterBuffer(binding); if (buffer.get() != nullptr) { BufferGL *bufferGL = GetImplAs(buffer.get()); if (buffer.getSize() == 0) { bindBufferBase(gl::BufferBinding::AtomicCounter, binding, bufferGL->getBufferID()); } else { bindBufferRange(gl::BufferBinding::AtomicCounter, binding, bufferGL->getBufferID(), buffer.getOffset(), buffer.getSize()); } } } } void StateManagerGL::updateProgramImageBindings(const gl::Context *context) { const gl::State &glState = context->getState(); const gl::ProgramExecutable *executable = glState.getProgramExecutable(); const gl::Program *program = glState.getProgram(); // It is possible there is no active program during a path operation. if (!executable || !program) return; ASSERT(context->getClientVersion() >= gl::ES_3_1 || program->getImageBindings().empty()); for (size_t imageUnitIndex : executable->getActiveImagesMask()) { const gl::ImageUnit &imageUnit = glState.getImageUnit(imageUnitIndex); const TextureGL *textureGL = SafeGetImplAs(imageUnit.texture.get()); if (textureGL) { bindImageTexture(imageUnitIndex, textureGL->getTextureID(), imageUnit.level, imageUnit.layered, imageUnit.layer, imageUnit.access, imageUnit.format); } else { bindImageTexture(imageUnitIndex, 0, imageUnit.level, imageUnit.layered, imageUnit.layer, imageUnit.access, imageUnit.format); } } } void StateManagerGL::setAttributeCurrentData(size_t index, const gl::VertexAttribCurrentValueData &data) { if (mVertexAttribCurrentValues[index] != data) { mVertexAttribCurrentValues[index] = data; switch (mVertexAttribCurrentValues[index].Type) { case gl::VertexAttribType::Float: mFunctions->vertexAttrib4fv(static_cast(index), mVertexAttribCurrentValues[index].Values.FloatValues); break; case gl::VertexAttribType::Int: mFunctions->vertexAttribI4iv(static_cast(index), mVertexAttribCurrentValues[index].Values.IntValues); break; case gl::VertexAttribType::UnsignedInt: mFunctions->vertexAttribI4uiv( static_cast(index), mVertexAttribCurrentValues[index].Values.UnsignedIntValues); break; default: UNREACHABLE(); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_CURRENT_VALUES); mLocalDirtyCurrentValues.set(index); } } void StateManagerGL::setScissorTestEnabled(bool enabled) { if (mScissorTestEnabled != enabled) { mScissorTestEnabled = enabled; if (mScissorTestEnabled) { mFunctions->enable(GL_SCISSOR_TEST); } else { mFunctions->disable(GL_SCISSOR_TEST); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED); } } void StateManagerGL::setScissor(const gl::Rectangle &scissor) { if (scissor != mScissor) { mScissor = scissor; mFunctions->scissor(mScissor.x, mScissor.y, mScissor.width, mScissor.height); mLocalDirtyBits.set(gl::State::DIRTY_BIT_SCISSOR); } } void StateManagerGL::setViewport(const gl::Rectangle &viewport) { if (viewport != mViewport) { mViewport = viewport; mFunctions->viewport(mViewport.x, mViewport.y, mViewport.width, mViewport.height); mLocalDirtyBits.set(gl::State::DIRTY_BIT_VIEWPORT); } } void StateManagerGL::setDepthRange(float near, float far) { mNear = near; mFar = far; // The glDepthRangef function isn't available until OpenGL 4.1. Prefer it when it is // available because OpenGL ES only works in floats. if (mFunctions->depthRangef) { mFunctions->depthRangef(mNear, mFar); } else { ASSERT(mFunctions->depthRange); mFunctions->depthRange(mNear, mFar); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_DEPTH_RANGE); } void StateManagerGL::setBlendEnabled(bool enabled) { const gl::DrawBufferMask mask = enabled ? mBlendStateExt.mMaxEnabledMask : gl::DrawBufferMask::Zero(); if (mBlendStateExt.mEnabledMask == mask) { return; } if (enabled) { mFunctions->enable(GL_BLEND); } else { mFunctions->disable(GL_BLEND); } mBlendStateExt.setEnabled(enabled); mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_ENABLED); } void StateManagerGL::setBlendEnabledIndexed(const gl::DrawBufferMask enabledMask) { if (mBlendStateExt.mEnabledMask == enabledMask) { return; } // Get DrawBufferMask of buffers with different blend enable state gl::DrawBufferMask diffMask = mBlendStateExt.mEnabledMask ^ enabledMask; const size_t diffCount = diffMask.count(); // Check if enabling or disabling blending for all buffers reduces the number of subsequent // indexed commands. Implicitly handles the case when the new blend enable state is the same for // all buffers. if (diffCount > 1) { // The number of indexed blend enable commands in case a mass disable is used. const size_t enabledCount = enabledMask.count(); // The mask and the number of indexed blend disable commands in case a mass enable is used. const gl::DrawBufferMask disabledMask = enabledMask ^ mBlendStateExt.mMaxEnabledMask; const size_t disabledCount = disabledMask.count(); if (enabledCount < diffCount && enabledCount <= disabledCount) { diffMask = enabledMask; mFunctions->disable(GL_BLEND); } else if (disabledCount < diffCount && disabledCount <= enabledCount) { diffMask = disabledMask; mFunctions->enable(GL_BLEND); } } for (size_t drawBufferIndex : diffMask) { if (enabledMask.test(drawBufferIndex)) { mFunctions->enablei(GL_BLEND, static_cast(drawBufferIndex)); } else { mFunctions->disablei(GL_BLEND, static_cast(drawBufferIndex)); } } mBlendStateExt.mEnabledMask = enabledMask; mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_ENABLED); } void StateManagerGL::setBlendColor(const gl::ColorF &blendColor) { if (mBlendColor != blendColor) { mBlendColor = blendColor; mFunctions->blendColor(mBlendColor.red, mBlendColor.green, mBlendColor.blue, mBlendColor.alpha); mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_COLOR); } } void StateManagerGL::setBlendFuncs(const gl::BlendStateExt &blendStateExt) { if (mBlendStateExt.mSrcColor == blendStateExt.mSrcColor && mBlendStateExt.mDstColor == blendStateExt.mDstColor && mBlendStateExt.mSrcAlpha == blendStateExt.mSrcAlpha && mBlendStateExt.mDstAlpha == blendStateExt.mDstAlpha) { return; } if (!mIndependentBlendStates) { mFunctions->blendFuncSeparate( blendStateExt.getSrcColorIndexed(0), blendStateExt.getDstColorIndexed(0), blendStateExt.getSrcAlphaIndexed(0), blendStateExt.getDstAlphaIndexed(0)); } else { // Get DrawBufferMask of buffers with different blend factors gl::DrawBufferMask diffMask = mBlendStateExt.compareFactors(blendStateExt.mSrcColor, blendStateExt.mDstColor, blendStateExt.mSrcAlpha, blendStateExt.mDstAlpha); size_t diffCount = diffMask.count(); // Check if setting all buffers to the same value reduces the number of subsequent indexed // commands. Implicitly handles the case when the new blend function state is the same for // all buffers. if (diffCount > 1) { bool found = false; gl::BlendStateExt::FactorStorage::Type commonSrcColor = 0; gl::BlendStateExt::FactorStorage::Type commonDstColor = 0; gl::BlendStateExt::FactorStorage::Type commonSrcAlpha = 0; gl::BlendStateExt::FactorStorage::Type commonDstAlpha = 0; for (size_t i = 0; i < mBlendStateExt.mMaxDrawBuffers - 1; i++) { const gl::BlendStateExt::FactorStorage::Type tempCommonSrcColor = blendStateExt.expandSrcColorIndexed(i); const gl::BlendStateExt::FactorStorage::Type tempCommonDstColor = blendStateExt.expandDstColorIndexed(i); const gl::BlendStateExt::FactorStorage::Type tempCommonSrcAlpha = blendStateExt.expandSrcAlphaIndexed(i); const gl::BlendStateExt::FactorStorage::Type tempCommonDstAlpha = blendStateExt.expandDstAlphaIndexed(i); const gl::DrawBufferMask tempDiffMask = blendStateExt.compareFactors( tempCommonSrcColor, tempCommonDstColor, tempCommonSrcAlpha, tempCommonDstAlpha); const size_t tempDiffCount = tempDiffMask.count(); if (tempDiffCount < diffCount) { found = true; diffMask = tempDiffMask; diffCount = tempDiffCount; commonSrcColor = tempCommonSrcColor; commonDstColor = tempCommonDstColor; commonSrcAlpha = tempCommonSrcAlpha; commonDstAlpha = tempCommonDstAlpha; if (tempDiffCount == 0) { break; // the blend factors are the same for all buffers } } } if (found) { mFunctions->blendFuncSeparate( ToGLenum(gl::BlendStateExt::FactorStorage::GetValueIndexed(0, commonSrcColor)), ToGLenum(gl::BlendStateExt::FactorStorage::GetValueIndexed(0, commonDstColor)), ToGLenum(gl::BlendStateExt::FactorStorage::GetValueIndexed(0, commonSrcAlpha)), ToGLenum(gl::BlendStateExt::FactorStorage::GetValueIndexed(0, commonDstAlpha))); } } for (size_t drawBufferIndex : diffMask) { mFunctions->blendFuncSeparatei(static_cast(drawBufferIndex), blendStateExt.getSrcColorIndexed(drawBufferIndex), blendStateExt.getDstColorIndexed(drawBufferIndex), blendStateExt.getSrcAlphaIndexed(drawBufferIndex), blendStateExt.getDstAlphaIndexed(drawBufferIndex)); } } mBlendStateExt.mSrcColor = blendStateExt.mSrcColor; mBlendStateExt.mDstColor = blendStateExt.mDstColor; mBlendStateExt.mSrcAlpha = blendStateExt.mSrcAlpha; mBlendStateExt.mDstAlpha = blendStateExt.mDstAlpha; mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_FUNCS); } void StateManagerGL::setBlendEquations(const gl::BlendStateExt &blendStateExt) { if (mBlendStateExt.mEquationColor == blendStateExt.mEquationColor && mBlendStateExt.mEquationAlpha == blendStateExt.mEquationAlpha) { return; } if (!mIndependentBlendStates) { mFunctions->blendEquationSeparate(blendStateExt.getEquationColorIndexed(0), blendStateExt.getEquationAlphaIndexed(0)); } else { // Get DrawBufferMask of buffers with different blend equations gl::DrawBufferMask diffMask = mBlendStateExt.compareEquations(blendStateExt.mEquationColor, blendStateExt.mEquationAlpha); size_t diffCount = diffMask.count(); // Check if setting all buffers to the same value reduces the number of subsequent indexed // commands. Implicitly handles the case when the new blend equation state is the same for // all buffers. if (diffCount > 1) { bool found = false; gl::BlendStateExt::EquationStorage::Type commonEquationColor = 0; gl::BlendStateExt::EquationStorage::Type commonEquationAlpha = 0; for (size_t i = 0; i < mBlendStateExt.mMaxDrawBuffers - 1; i++) { const gl::BlendStateExt::EquationStorage::Type tempCommonEquationColor = blendStateExt.expandEquationColorIndexed(i); const gl::BlendStateExt::EquationStorage::Type tempCommonEquationAlpha = blendStateExt.expandEquationAlphaIndexed(i); const gl::DrawBufferMask tempDiffMask = blendStateExt.compareEquations( tempCommonEquationColor, tempCommonEquationAlpha); const size_t tempDiffCount = tempDiffMask.count(); if (tempDiffCount < diffCount) { found = true; diffMask = tempDiffMask; diffCount = tempDiffCount; commonEquationColor = tempCommonEquationColor; commonEquationAlpha = tempCommonEquationAlpha; if (tempDiffCount == 0) { break; // the new blend equations are the same for all buffers } } } if (found) { mFunctions->blendEquationSeparate( ToGLenum(gl::BlendStateExt::EquationStorage::GetValueIndexed( 0, commonEquationColor)), ToGLenum(gl::BlendStateExt::EquationStorage::GetValueIndexed( 0, commonEquationAlpha))); } } for (size_t drawBufferIndex : diffMask) { mFunctions->blendEquationSeparatei( static_cast(drawBufferIndex), blendStateExt.getEquationColorIndexed(drawBufferIndex), blendStateExt.getEquationAlphaIndexed(drawBufferIndex)); } } mBlendStateExt.mEquationColor = blendStateExt.mEquationColor; mBlendStateExt.mEquationAlpha = blendStateExt.mEquationAlpha; mLocalDirtyBits.set(gl::State::DIRTY_BIT_COLOR_MASK); } void StateManagerGL::setColorMask(bool red, bool green, bool blue, bool alpha) { const gl::BlendStateExt::ColorMaskStorage::Type mask = mBlendStateExt.expandColorMaskValue(red, green, blue, alpha); if (mBlendStateExt.mColorMask != mask) { mFunctions->colorMask(red, green, blue, alpha); mBlendStateExt.mColorMask = mask; mLocalDirtyBits.set(gl::State::DIRTY_BIT_COLOR_MASK); } } void StateManagerGL::setSampleAlphaToCoverageEnabled(bool enabled) { if (mSampleAlphaToCoverageEnabled != enabled) { mSampleAlphaToCoverageEnabled = enabled; if (mSampleAlphaToCoverageEnabled) { mFunctions->enable(GL_SAMPLE_ALPHA_TO_COVERAGE); } else { mFunctions->disable(GL_SAMPLE_ALPHA_TO_COVERAGE); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED); } } void StateManagerGL::setSampleCoverageEnabled(bool enabled) { if (mSampleCoverageEnabled != enabled) { mSampleCoverageEnabled = enabled; if (mSampleCoverageEnabled) { mFunctions->enable(GL_SAMPLE_COVERAGE); } else { mFunctions->disable(GL_SAMPLE_COVERAGE); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED); } } void StateManagerGL::setSampleCoverage(float value, bool invert) { if (mSampleCoverageValue != value || mSampleCoverageInvert != invert) { mSampleCoverageValue = value; mSampleCoverageInvert = invert; mFunctions->sampleCoverage(mSampleCoverageValue, mSampleCoverageInvert); mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_COVERAGE); } } void StateManagerGL::setSampleMaskEnabled(bool enabled) { if (mSampleMaskEnabled != enabled) { mSampleMaskEnabled = enabled; if (mSampleMaskEnabled) { mFunctions->enable(GL_SAMPLE_MASK); } else { mFunctions->disable(GL_SAMPLE_MASK); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_MASK_ENABLED); } } void StateManagerGL::setSampleMaski(GLuint maskNumber, GLbitfield mask) { ASSERT(maskNumber < mSampleMaskValues.size()); if (mSampleMaskValues[maskNumber] != mask) { mSampleMaskValues[maskNumber] = mask; mFunctions->sampleMaski(maskNumber, mask); mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_MASK); } } // Depth and stencil redundant state changes are guarded in the // frontend so for related cases here just set the dirty bit // and update backend states. void StateManagerGL::setDepthTestEnabled(bool enabled) { mDepthTestEnabled = enabled; if (mDepthTestEnabled) { mFunctions->enable(GL_DEPTH_TEST); } else { mFunctions->disable(GL_DEPTH_TEST); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED); } void StateManagerGL::setDepthFunc(GLenum depthFunc) { mDepthFunc = depthFunc; mFunctions->depthFunc(mDepthFunc); mLocalDirtyBits.set(gl::State::DIRTY_BIT_DEPTH_FUNC); } void StateManagerGL::setDepthMask(bool mask) { mDepthMask = mask; mFunctions->depthMask(mDepthMask); mLocalDirtyBits.set(gl::State::DIRTY_BIT_DEPTH_MASK); } void StateManagerGL::setStencilTestEnabled(bool enabled) { mStencilTestEnabled = enabled; if (mStencilTestEnabled) { mFunctions->enable(GL_STENCIL_TEST); } else { mFunctions->disable(GL_STENCIL_TEST); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED); } void StateManagerGL::setStencilFrontWritemask(GLuint mask) { mStencilFrontWritemask = mask; mFunctions->stencilMaskSeparate(GL_FRONT, mStencilFrontWritemask); mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT); } void StateManagerGL::setStencilBackWritemask(GLuint mask) { mStencilBackWritemask = mask; mFunctions->stencilMaskSeparate(GL_BACK, mStencilBackWritemask); mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK); } void StateManagerGL::setStencilFrontFuncs(GLenum func, GLint ref, GLuint mask) { mStencilFrontFunc = func; mStencilFrontRef = ref; mStencilFrontValueMask = mask; mFunctions->stencilFuncSeparate(GL_FRONT, mStencilFrontFunc, mStencilFrontRef, mStencilFrontValueMask); mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT); } void StateManagerGL::setStencilBackFuncs(GLenum func, GLint ref, GLuint mask) { mStencilBackFunc = func; mStencilBackRef = ref; mStencilBackValueMask = mask; mFunctions->stencilFuncSeparate(GL_BACK, mStencilBackFunc, mStencilBackRef, mStencilBackValueMask); mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK); } void StateManagerGL::setStencilFrontOps(GLenum sfail, GLenum dpfail, GLenum dppass) { mStencilFrontStencilFailOp = sfail; mStencilFrontStencilPassDepthFailOp = dpfail; mStencilFrontStencilPassDepthPassOp = dppass; mFunctions->stencilOpSeparate(GL_FRONT, mStencilFrontStencilFailOp, mStencilFrontStencilPassDepthFailOp, mStencilFrontStencilPassDepthPassOp); mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_OPS_FRONT); } void StateManagerGL::setStencilBackOps(GLenum sfail, GLenum dpfail, GLenum dppass) { mStencilBackStencilFailOp = sfail; mStencilBackStencilPassDepthFailOp = dpfail; mStencilBackStencilPassDepthPassOp = dppass; mFunctions->stencilOpSeparate(GL_BACK, mStencilBackStencilFailOp, mStencilBackStencilPassDepthFailOp, mStencilBackStencilPassDepthPassOp); mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_OPS_BACK); } void StateManagerGL::setCullFaceEnabled(bool enabled) { if (mCullFaceEnabled != enabled) { mCullFaceEnabled = enabled; if (mCullFaceEnabled) { mFunctions->enable(GL_CULL_FACE); } else { mFunctions->disable(GL_CULL_FACE); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_CULL_FACE_ENABLED); } } void StateManagerGL::setCullFace(gl::CullFaceMode cullFace) { if (mCullFace != cullFace) { mCullFace = cullFace; mFunctions->cullFace(ToGLenum(mCullFace)); mLocalDirtyBits.set(gl::State::DIRTY_BIT_CULL_FACE); } } void StateManagerGL::setFrontFace(GLenum frontFace) { if (mFrontFace != frontFace) { mFrontFace = frontFace; mFunctions->frontFace(mFrontFace); mLocalDirtyBits.set(gl::State::DIRTY_BIT_FRONT_FACE); } } void StateManagerGL::setPolygonOffsetFillEnabled(bool enabled) { if (mPolygonOffsetFillEnabled != enabled) { mPolygonOffsetFillEnabled = enabled; if (mPolygonOffsetFillEnabled) { mFunctions->enable(GL_POLYGON_OFFSET_FILL); } else { mFunctions->disable(GL_POLYGON_OFFSET_FILL); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED); } } void StateManagerGL::setPolygonOffset(float factor, float units) { if (mPolygonOffsetFactor != factor || mPolygonOffsetUnits != units) { mPolygonOffsetFactor = factor; mPolygonOffsetUnits = units; mFunctions->polygonOffset(mPolygonOffsetFactor, mPolygonOffsetUnits); mLocalDirtyBits.set(gl::State::DIRTY_BIT_POLYGON_OFFSET); } } void StateManagerGL::setRasterizerDiscardEnabled(bool enabled) { if (mRasterizerDiscardEnabled != enabled) { mRasterizerDiscardEnabled = enabled; if (mRasterizerDiscardEnabled) { mFunctions->enable(GL_RASTERIZER_DISCARD); } else { mFunctions->disable(GL_RASTERIZER_DISCARD); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED); } } void StateManagerGL::setLineWidth(float width) { if (mLineWidth != width) { mLineWidth = width; mFunctions->lineWidth(mLineWidth); mLocalDirtyBits.set(gl::State::DIRTY_BIT_LINE_WIDTH); } } angle::Result StateManagerGL::setPrimitiveRestartEnabled(const gl::Context *context, bool enabled) { if (mPrimitiveRestartEnabled != enabled) { GLenum cap = mFeatures.emulatePrimitiveRestartFixedIndex.enabled ? GL_PRIMITIVE_RESTART : GL_PRIMITIVE_RESTART_FIXED_INDEX; if (enabled) { ANGLE_GL_TRY(context, mFunctions->enable(cap)); } else { ANGLE_GL_TRY(context, mFunctions->disable(cap)); } mPrimitiveRestartEnabled = enabled; mLocalDirtyBits.set(gl::State::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED); } return angle::Result::Continue; } angle::Result StateManagerGL::setPrimitiveRestartIndex(const gl::Context *context, GLuint index) { if (mPrimitiveRestartIndex != index) { ANGLE_GL_TRY(context, mFunctions->primitiveRestartIndex(index)); mPrimitiveRestartIndex = index; // No dirty bit for this state, it is not exposed to the frontend. } return angle::Result::Continue; } void StateManagerGL::setClearDepth(float clearDepth) { if (mClearDepth != clearDepth) { mClearDepth = clearDepth; // The glClearDepthf function isn't available until OpenGL 4.1. Prefer it when it is // available because OpenGL ES only works in floats. if (mFunctions->clearDepthf) { mFunctions->clearDepthf(mClearDepth); } else { ASSERT(mFunctions->clearDepth); mFunctions->clearDepth(mClearDepth); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_CLEAR_DEPTH); } } void StateManagerGL::setClearColor(const gl::ColorF &clearColor) { gl::ColorF modifiedClearColor = clearColor; if (mFeatures.clearToZeroOrOneBroken.enabled && (clearColor.red == 1.0f || clearColor.red == 0.0f) && (clearColor.green == 1.0f || clearColor.green == 0.0f) && (clearColor.blue == 1.0f || clearColor.blue == 0.0f) && (clearColor.alpha == 1.0f || clearColor.alpha == 0.0f)) { if (clearColor.alpha == 1.0f) { modifiedClearColor.alpha = 2.0f; } else { modifiedClearColor.alpha = -1.0f; } } if (mClearColor != modifiedClearColor) { mClearColor = modifiedClearColor; mFunctions->clearColor(mClearColor.red, mClearColor.green, mClearColor.blue, mClearColor.alpha); mLocalDirtyBits.set(gl::State::DIRTY_BIT_CLEAR_COLOR); } } void StateManagerGL::setClearStencil(GLint clearStencil) { if (mClearStencil != clearStencil) { mClearStencil = clearStencil; mFunctions->clearStencil(mClearStencil); mLocalDirtyBits.set(gl::State::DIRTY_BIT_CLEAR_STENCIL); } } angle::Result StateManagerGL::syncState(const gl::Context *context, const gl::State::DirtyBits &glDirtyBits, const gl::State::DirtyBits &bitMask) { const gl::State &state = context->getState(); const gl::State::DirtyBits glAndLocalDirtyBits = (glDirtyBits | mLocalDirtyBits) & bitMask; if (!glAndLocalDirtyBits.any()) { return angle::Result::Continue; } // TODO(jmadill): Investigate only syncing vertex state for active attributes for (auto iter = glAndLocalDirtyBits.begin(), endIter = glAndLocalDirtyBits.end(); iter != endIter; ++iter) { switch (*iter) { case gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED: setScissorTestEnabled(state.isScissorTestEnabled()); break; case gl::State::DIRTY_BIT_SCISSOR: { const gl::Rectangle &scissor = state.getScissor(); setScissor(scissor); } break; case gl::State::DIRTY_BIT_VIEWPORT: { const gl::Rectangle &viewport = state.getViewport(); setViewport(viewport); } break; case gl::State::DIRTY_BIT_DEPTH_RANGE: setDepthRange(state.getNearPlane(), state.getFarPlane()); break; case gl::State::DIRTY_BIT_BLEND_ENABLED: if (mIndependentBlendStates) { setBlendEnabledIndexed(state.getBlendEnabledDrawBufferMask()); } else { setBlendEnabled(state.isBlendEnabled()); } break; case gl::State::DIRTY_BIT_BLEND_COLOR: setBlendColor(state.getBlendColor()); break; case gl::State::DIRTY_BIT_BLEND_FUNCS: { setBlendFuncs(state.getBlendStateExt()); break; } case gl::State::DIRTY_BIT_BLEND_EQUATIONS: { setBlendEquations(state.getBlendStateExt()); break; } case gl::State::DIRTY_BIT_COLOR_MASK: { const gl::Framebuffer *framebuffer = state.getDrawFramebuffer(); const FramebufferGL *framebufferGL = GetImplAs(framebuffer); const bool disableAlphaWrite = framebufferGL->hasEmulatedAlphaChannelTextureAttachment(); setColorMaskForFramebuffer(state.getBlendStateExt(), disableAlphaWrite); break; } case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED: setSampleAlphaToCoverageEnabled(state.isSampleAlphaToCoverageEnabled()); break; case gl::State::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED: setSampleCoverageEnabled(state.isSampleCoverageEnabled()); break; case gl::State::DIRTY_BIT_SAMPLE_COVERAGE: setSampleCoverage(state.getSampleCoverageValue(), state.getSampleCoverageInvert()); break; case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED: setDepthTestEnabled(state.isDepthTestEnabled()); break; case gl::State::DIRTY_BIT_DEPTH_FUNC: setDepthFunc(state.getDepthStencilState().depthFunc); break; case gl::State::DIRTY_BIT_DEPTH_MASK: setDepthMask(state.getDepthStencilState().depthMask); break; case gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED: setStencilTestEnabled(state.isStencilTestEnabled()); break; case gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT: { const auto &depthStencilState = state.getDepthStencilState(); setStencilFrontFuncs(depthStencilState.stencilFunc, state.getStencilRef(), depthStencilState.stencilMask); break; } case gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK: { const auto &depthStencilState = state.getDepthStencilState(); setStencilBackFuncs(depthStencilState.stencilBackFunc, state.getStencilBackRef(), depthStencilState.stencilBackMask); break; } case gl::State::DIRTY_BIT_STENCIL_OPS_FRONT: { const auto &depthStencilState = state.getDepthStencilState(); setStencilFrontOps(depthStencilState.stencilFail, depthStencilState.stencilPassDepthFail, depthStencilState.stencilPassDepthPass); break; } case gl::State::DIRTY_BIT_STENCIL_OPS_BACK: { const auto &depthStencilState = state.getDepthStencilState(); setStencilBackOps(depthStencilState.stencilBackFail, depthStencilState.stencilBackPassDepthFail, depthStencilState.stencilBackPassDepthPass); break; } case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT: setStencilFrontWritemask(state.getDepthStencilState().stencilWritemask); break; case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK: setStencilBackWritemask(state.getDepthStencilState().stencilBackWritemask); break; case gl::State::DIRTY_BIT_CULL_FACE_ENABLED: setCullFaceEnabled(state.isCullFaceEnabled()); break; case gl::State::DIRTY_BIT_CULL_FACE: setCullFace(state.getRasterizerState().cullMode); break; case gl::State::DIRTY_BIT_FRONT_FACE: setFrontFace(state.getRasterizerState().frontFace); break; case gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED: setPolygonOffsetFillEnabled(state.isPolygonOffsetFillEnabled()); break; case gl::State::DIRTY_BIT_POLYGON_OFFSET: { const auto &rasterizerState = state.getRasterizerState(); setPolygonOffset(rasterizerState.polygonOffsetFactor, rasterizerState.polygonOffsetUnits); break; } case gl::State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED: setRasterizerDiscardEnabled(state.isRasterizerDiscardEnabled()); break; case gl::State::DIRTY_BIT_LINE_WIDTH: setLineWidth(state.getLineWidth()); break; case gl::State::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED: ANGLE_TRY(setPrimitiveRestartEnabled(context, state.isPrimitiveRestartEnabled())); break; case gl::State::DIRTY_BIT_CLEAR_COLOR: setClearColor(state.getColorClearValue()); break; case gl::State::DIRTY_BIT_CLEAR_DEPTH: setClearDepth(state.getDepthClearValue()); break; case gl::State::DIRTY_BIT_CLEAR_STENCIL: setClearStencil(state.getStencilClearValue()); break; case gl::State::DIRTY_BIT_UNPACK_STATE: ANGLE_TRY(setPixelUnpackState(context, state.getUnpackState())); break; case gl::State::DIRTY_BIT_UNPACK_BUFFER_BINDING: ANGLE_TRY(setPixelUnpackBuffer( context, state.getTargetBuffer(gl::BufferBinding::PixelUnpack))); break; case gl::State::DIRTY_BIT_PACK_STATE: ANGLE_TRY(setPixelPackState(context, state.getPackState())); break; case gl::State::DIRTY_BIT_PACK_BUFFER_BINDING: ANGLE_TRY(setPixelPackBuffer(context, state.getTargetBuffer(gl::BufferBinding::PixelPack))); break; case gl::State::DIRTY_BIT_DITHER_ENABLED: setDitherEnabled(state.isDitherEnabled()); break; case gl::State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING: { gl::Framebuffer *framebuffer = state.getReadFramebuffer(); // Necessary for an Intel TexImage workaround. if (!framebuffer) continue; FramebufferGL *framebufferGL = GetImplAs(framebuffer); bindFramebuffer( mHasSeparateFramebufferBindings ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER, framebufferGL->getFramebufferID()); break; } case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING: { gl::Framebuffer *framebuffer = state.getDrawFramebuffer(); // Necessary for an Intel TexImage workaround. if (!framebuffer) continue; FramebufferGL *framebufferGL = GetImplAs(framebuffer); bindFramebuffer( mHasSeparateFramebufferBindings ? GL_DRAW_FRAMEBUFFER : GL_FRAMEBUFFER, framebufferGL->getFramebufferID()); const gl::Program *program = state.getProgram(); if (program) { updateMultiviewBaseViewLayerIndexUniform(program, framebufferGL->getState()); } // Changing the draw framebuffer binding sometimes requires resetting srgb blending. iter.setLaterBit(gl::State::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE); // If the framebuffer is emulating RGB on top of RGBA, the color mask has to be // updated iter.setLaterBit(gl::State::DIRTY_BIT_COLOR_MASK); break; } case gl::State::DIRTY_BIT_RENDERBUFFER_BINDING: // TODO(jmadill): implement this break; case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING: { VertexArrayGL *vaoGL = GetImplAs(state.getVertexArray()); bindVertexArray(vaoGL->getVertexArrayID(), vaoGL->getNativeState()); propagateProgramToVAO(context, state.getProgram(), GetImplAs(state.getVertexArray())); if (mFeatures.syncVertexArraysToDefault.enabled) { // Re-sync the vertex array because all frontend VAOs share the same backend // state. Only sync bits that can be set in ES2.0 or 3.0 gl::VertexArray::DirtyBits dirtyBits; gl::VertexArray::DirtyAttribBitsArray dirtyAttribBits; gl::VertexArray::DirtyBindingBitsArray dirtBindingBits; dirtyBits.set(gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER); for (size_t attrib = 0; attrib < mDefaultVAOState.attributes.size(); attrib++) { dirtyBits.set(gl::VertexArray::DIRTY_BIT_ATTRIB_0 + attrib); dirtyAttribBits[attrib].set(gl::VertexArray::DIRTY_ATTRIB_ENABLED); dirtyAttribBits[attrib].set(gl::VertexArray::DIRTY_ATTRIB_POINTER); dirtyAttribBits[attrib].set(gl::VertexArray::DIRTY_ATTRIB_POINTER_BUFFER); } for (size_t binding = 0; binding < mDefaultVAOState.bindings.size(); binding++) { dirtyBits.set(gl::VertexArray::DIRTY_BIT_BINDING_0 + binding); dirtBindingBits[binding].set(gl::VertexArray::DIRTY_BINDING_DIVISOR); } ANGLE_TRY( vaoGL->syncState(context, dirtyBits, &dirtyAttribBits, &dirtBindingBits)); } break; } case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING: updateDrawIndirectBufferBinding(context); break; case gl::State::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING: updateDispatchIndirectBufferBinding(context); break; case gl::State::DIRTY_BIT_PROGRAM_BINDING: { gl::Program *program = state.getProgram(); if (program != nullptr) { useProgram(GetImplAs(program)->getProgramID()); } break; } case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE: { const gl::Program *program = state.getProgram(); const gl::ProgramExecutable *executable = state.getProgramExecutable(); if (program && executable) { iter.setLaterBit(gl::State::DIRTY_BIT_TEXTURE_BINDINGS); if (executable->getActiveImagesMask().any()) { iter.setLaterBit(gl::State::DIRTY_BIT_IMAGE_BINDINGS); } if (program->getActiveShaderStorageBlockCount() > 0) { iter.setLaterBit(gl::State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING); } if (program->getActiveUniformBlockCount() > 0) { iter.setLaterBit(gl::State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS); } if (program->getActiveAtomicCounterBufferCount() > 0) { iter.setLaterBit(gl::State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING); } if (mIsMultiviewEnabled && program->usesMultiview()) { updateMultiviewBaseViewLayerIndexUniform( program, state.getDrawFramebuffer()->getImplementation()->getState()); } } if (!program || !program->getExecutable().hasLinkedShaderStage(gl::ShaderType::Compute)) { propagateProgramToVAO(context, program, GetImplAs(state.getVertexArray())); } break; } case gl::State::DIRTY_BIT_TEXTURE_BINDINGS: updateProgramTextureBindings(context); break; case gl::State::DIRTY_BIT_SAMPLER_BINDINGS: syncSamplersState(context); break; case gl::State::DIRTY_BIT_IMAGE_BINDINGS: updateProgramImageBindings(context); break; case gl::State::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING: syncTransformFeedbackState(context); break; case gl::State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING: updateProgramStorageBufferBindings(context); break; case gl::State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS: updateProgramUniformBufferBindings(context); break; case gl::State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING: updateProgramAtomicCounterBufferBindings(context); break; case gl::State::DIRTY_BIT_MULTISAMPLING: setMultisamplingStateEnabled(state.isMultisamplingEnabled()); break; case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE: setSampleAlphaToOneStateEnabled(state.isSampleAlphaToOneEnabled()); break; case gl::State::DIRTY_BIT_COVERAGE_MODULATION: setCoverageModulation(state.getCoverageModulation()); break; case gl::State::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE: setFramebufferSRGBEnabledForFramebuffer( context, state.getFramebufferSRGB(), GetImplAs(state.getDrawFramebuffer())); break; case gl::State::DIRTY_BIT_SAMPLE_MASK_ENABLED: setSampleMaskEnabled(state.isSampleMaskEnabled()); break; case gl::State::DIRTY_BIT_SAMPLE_MASK: { for (GLuint maskNumber = 0; maskNumber < state.getMaxSampleMaskWords(); ++maskNumber) { setSampleMaski(maskNumber, state.getSampleMaskWord(maskNumber)); } break; } case gl::State::DIRTY_BIT_CURRENT_VALUES: { gl::AttributesMask combinedMask = (state.getAndResetDirtyCurrentValues() | mLocalDirtyCurrentValues); mLocalDirtyCurrentValues.reset(); for (auto attribIndex : combinedMask) { setAttributeCurrentData(attribIndex, state.getVertexAttribCurrentValue(attribIndex)); } break; } case gl::State::DIRTY_BIT_PROVOKING_VERTEX: setProvokingVertex(ToGLenum(state.getProvokingVertex())); break; case gl::State::DIRTY_BIT_EXTENDED: // Handling clip distance enabled flags: setClipDistancesEnable(state.getEnabledClipDistances()); // TODO(jmadill): handle mipmap generation hint // TODO(jmadill): handle shader derivative hint // Nothing to do until EXT_clip_contorl is implemented. break; case gl::State::DIRTY_BIT_SAMPLE_SHADING: // Nothing to do until OES_sample_shading is implemented. break; case gl::State::DIRTY_BIT_PATCH_VERTICES: // Nothing to do until EXT_tessellation_shader is implemented. break; default: UNREACHABLE(); break; } } mLocalDirtyBits &= ~(bitMask); return angle::Result::Continue; } void StateManagerGL::setFramebufferSRGBEnabled(const gl::Context *context, bool enabled) { if (!mFramebufferSRGBAvailable) { return; } if (mFramebufferSRGBEnabled != enabled) { mFramebufferSRGBEnabled = enabled; if (mFramebufferSRGBEnabled) { mFunctions->enable(GL_FRAMEBUFFER_SRGB); } else { mFunctions->disable(GL_FRAMEBUFFER_SRGB); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE); } } void StateManagerGL::setFramebufferSRGBEnabledForFramebuffer(const gl::Context *context, bool enabled, const FramebufferGL *framebuffer) { if (framebuffer->isDefault()) { // Obey the framebuffer sRGB state for blending on all framebuffers except the default // framebuffer. // When SRGB blending is enabled, only SRGB capable formats will use it but the default // framebuffer will always use it if it is enabled. // TODO(geofflang): Update this when the framebuffer binding dirty changes, when it exists. setFramebufferSRGBEnabled(context, false); } else { setFramebufferSRGBEnabled(context, enabled); } } void StateManagerGL::setColorMaskForFramebuffer(const gl::BlendStateExt &blendStateExt, const bool disableAlpha) { bool r, g, b, a; // Given that disableAlpha can be true only on macOS backbuffers and color mask is re-synced on // bound draw framebuffer change, switch all draw buffers color masks to avoid special case // later. if (!mIndependentBlendStates || disableAlpha) { blendStateExt.getColorMaskIndexed(0, &r, &g, &b, &a); setColorMask(r, g, b, disableAlpha ? false : a); return; } // Check if the current mask already matches the new state if (mBlendStateExt.mColorMask == blendStateExt.mColorMask) { return; } // Get DrawBufferMask of buffers with different color masks gl::DrawBufferMask diffMask = mBlendStateExt.compareColorMask(blendStateExt.mColorMask); size_t diffCount = diffMask.count(); // Check if setting all buffers to the same value reduces the number of subsequent indexed // commands. Implicitly handles the case when the new mask is the same for all buffers. // For instance, let's say that previously synced mask is ccccff00 and the new state is // ffeeeeee. Instead of calling colorMaski 8 times, ANGLE can set all buffers to `e` and then // use colorMaski only twice. On the other hand, if the new state is cceeee00, a non-indexed // call will increase the total number of GL commands. if (diffCount > 1) { bool found = false; gl::BlendStateExt::ColorMaskStorage::Type commonColorMask = 0; for (size_t i = 0; i < mBlendStateExt.mMaxDrawBuffers - 1; i++) { const gl::BlendStateExt::ColorMaskStorage::Type tempCommonColorMask = blendStateExt.expandColorMaskIndexed(i); const gl::DrawBufferMask tempDiffMask = blendStateExt.compareColorMask(tempCommonColorMask); const size_t tempDiffCount = tempDiffMask.count(); if (tempDiffCount < diffCount) { found = true; diffMask = tempDiffMask; diffCount = tempDiffCount; commonColorMask = tempCommonColorMask; if (tempDiffCount == 0) { break; // the new mask is the same for all buffers } } } if (found) { gl::BlendStateExt::UnpackColorMask(commonColorMask, &r, &g, &b, &a); mFunctions->colorMask(r, g, b, a); } } for (size_t drawBufferIndex : diffMask) { blendStateExt.getColorMaskIndexed(drawBufferIndex, &r, &g, &b, &a); mFunctions->colorMaski(static_cast(drawBufferIndex), r, g, b, a); } mBlendStateExt.mColorMask = blendStateExt.mColorMask; mLocalDirtyBits.set(gl::State::DIRTY_BIT_COLOR_MASK); } void StateManagerGL::setDitherEnabled(bool enabled) { if (mDitherEnabled != enabled) { mDitherEnabled = enabled; if (mDitherEnabled) { mFunctions->enable(GL_DITHER); } else { mFunctions->disable(GL_DITHER); } } } void StateManagerGL::setMultisamplingStateEnabled(bool enabled) { if (mMultisamplingEnabled != enabled) { mMultisamplingEnabled = enabled; if (mMultisamplingEnabled) { mFunctions->enable(GL_MULTISAMPLE_EXT); } else { mFunctions->disable(GL_MULTISAMPLE_EXT); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_MULTISAMPLING); } } void StateManagerGL::setSampleAlphaToOneStateEnabled(bool enabled) { if (mSampleAlphaToOneEnabled != enabled) { mSampleAlphaToOneEnabled = enabled; if (mSampleAlphaToOneEnabled) { mFunctions->enable(GL_SAMPLE_ALPHA_TO_ONE); } else { mFunctions->disable(GL_SAMPLE_ALPHA_TO_ONE); } mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE); } } void StateManagerGL::setCoverageModulation(GLenum components) { if (mCoverageModulation != components) { mCoverageModulation = components; mFunctions->coverageModulationNV(components); mLocalDirtyBits.set(gl::State::DIRTY_BIT_COVERAGE_MODULATION); } } void StateManagerGL::setProvokingVertex(GLenum mode) { if (mode != mProvokingVertex) { mFunctions->provokingVertex(mode); mProvokingVertex = mode; mLocalDirtyBits.set(gl::State::DIRTY_BIT_PROVOKING_VERTEX); } } void StateManagerGL::setClipDistancesEnable(const gl::State::ClipDistanceEnableBits &enables) { if (enables == mEnabledClipDistances) { return; } ASSERT(mMaxClipDistances <= gl::IMPLEMENTATION_MAX_CLIP_DISTANCES); gl::State::ClipDistanceEnableBits diff = enables ^ mEnabledClipDistances; for (size_t i : diff) { if (enables.test(i)) { mFunctions->enable(GL_CLIP_DISTANCE0_EXT + static_cast(i)); } else { mFunctions->disable(GL_CLIP_DISTANCE0_EXT + static_cast(i)); } } mEnabledClipDistances = enables; mLocalDirtyBits.set(gl::State::DIRTY_BIT_EXTENDED); } void StateManagerGL::setTextureCubemapSeamlessEnabled(bool enabled) { // TODO(jmadill): Also check for seamless extension. if (!mFunctions->isAtLeastGL(gl::Version(3, 2))) { return; } if (mTextureCubemapSeamlessEnabled != enabled) { mTextureCubemapSeamlessEnabled = enabled; if (mTextureCubemapSeamlessEnabled) { mFunctions->enable(GL_TEXTURE_CUBE_MAP_SEAMLESS); } else { mFunctions->disable(GL_TEXTURE_CUBE_MAP_SEAMLESS); } } } void StateManagerGL::propagateProgramToVAO(const gl::Context *context, const gl::Program *program, VertexArrayGL *vao) { if (vao == nullptr) { return; } // Number of views: if (mIsMultiviewEnabled) { int programNumViews = 1; if (program && program->usesMultiview()) { programNumViews = program->getNumViews(); } vao->applyNumViewsToDivisor(context, programNumViews); } // Attribute enabled mask: if (program) { vao->applyActiveAttribLocationsMask( context, program->getExecutable().getActiveAttribLocationsMask()); } } void StateManagerGL::updateMultiviewBaseViewLayerIndexUniformImpl( const gl::Program *program, const gl::FramebufferState &drawFramebufferState) const { ASSERT(mIsMultiviewEnabled && program && program->usesMultiview()); const ProgramGL *programGL = GetImplAs(program); if (drawFramebufferState.isMultiview()) { programGL->enableLayeredRenderingPath(drawFramebufferState.getBaseViewIndex()); } } void StateManagerGL::syncSamplersState(const gl::Context *context) { const gl::SamplerBindingVector &samplers = context->getState().getSamplers(); // This could be optimized by using a separate binding dirty bit per sampler. for (size_t samplerIndex = 0; samplerIndex < samplers.size(); ++samplerIndex) { const gl::Sampler *sampler = samplers[samplerIndex].get(); if (sampler != nullptr) { SamplerGL *samplerGL = GetImplAs(sampler); bindSampler(samplerIndex, samplerGL->getSamplerID()); } else { bindSampler(samplerIndex, 0); } } } void StateManagerGL::syncTransformFeedbackState(const gl::Context *context) { // Set the current transform feedback state gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback(); if (transformFeedback) { TransformFeedbackGL *transformFeedbackGL = GetImplAs(transformFeedback); bindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedbackGL->getTransformFeedbackID()); transformFeedbackGL->syncActiveState(context, transformFeedback->isActive(), transformFeedback->getPrimitiveMode()); transformFeedbackGL->syncPausedState(transformFeedback->isPaused()); mCurrentTransformFeedback = transformFeedbackGL; } else { bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); mCurrentTransformFeedback = nullptr; } } GLuint StateManagerGL::getDefaultVAO() const { return mDefaultVAO; } VertexArrayStateGL *StateManagerGL::getDefaultVAOState() { return &mDefaultVAOState; } void StateManagerGL::validateState() const { // Current program ValidateStateHelper(mFunctions, mProgram, GL_CURRENT_PROGRAM, "mProgram", "GL_CURRENT_PROGRAM"); // Buffers for (gl::BufferBinding bindingType : angle::AllEnums()) { // These binding types need compute support to be queried if (bindingType == gl::BufferBinding::AtomicCounter || bindingType == gl::BufferBinding::DispatchIndirect || bindingType == gl::BufferBinding::ShaderStorage) { if (!nativegl::SupportsCompute(mFunctions)) { continue; } } // Transform feedback buffer bindings are tracked in TransformFeedbackGL if (bindingType == gl::BufferBinding::TransformFeedback) { continue; } GLenum bindingTypeGL = nativegl::GetBufferBindingQuery(bindingType); std::string localName = "mBuffers[" + ToString(bindingType) + "]"; ValidateStateHelper(mFunctions, mBuffers[bindingType], bindingTypeGL, localName.c_str(), nativegl::GetBufferBindingString(bindingType).c_str()); } // Vertex array object ValidateStateHelper(mFunctions, mVAO, GL_VERTEX_ARRAY_BINDING, "mVAO", "GL_VERTEX_ARRAY_BINDING"); } template <> void StateManagerGL::get(GLenum name, GLboolean *value) { mFunctions->getBooleanv(name, value); } template <> void StateManagerGL::get(GLenum name, bool *value) { GLboolean v; get(name, &v); *value = (v == GL_TRUE); } template <> void StateManagerGL::get(GLenum name, std::array *values) { GLboolean v[4]; get(name, v); for (size_t i = 0; i < 4; i++) { (*values)[i] = (v[i] == GL_TRUE); } } template <> void StateManagerGL::get(GLenum name, GLint *value) { mFunctions->getIntegerv(name, value); } template <> void StateManagerGL::get(GLenum name, GLenum *value) { GLint v; get(name, &v); *value = static_cast(v); } template <> void StateManagerGL::get(GLenum name, gl::Rectangle *rect) { GLint v[4]; get(name, v); *rect = gl::Rectangle(v[0], v[1], v[2], v[3]); } template <> void StateManagerGL::get(GLenum name, GLfloat *value) { mFunctions->getFloatv(name, value); } template <> void StateManagerGL::get(GLenum name, gl::ColorF *color) { GLfloat v[4]; get(name, v); *color = gl::ColorF(v[0], v[1], v[2], v[3]); } void StateManagerGL::syncFromNativeContext(const gl::Extensions &extensions, ExternalContextState *state) { ASSERT(mFunctions->getError() == GL_NO_ERROR); get(GL_VIEWPORT, &state->viewport); if (mViewport != state->viewport) { mViewport = state->viewport; mLocalDirtyBits.set(gl::State::DIRTY_BIT_VIEWPORT); } get(GL_SCISSOR_TEST, &state->scissorTest); if (mScissorTestEnabled != static_cast(state->scissorTest)) { mScissorTestEnabled = state->scissorTest; mLocalDirtyBits.set(gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED); } get(GL_SCISSOR_BOX, &state->scissorBox); if (mScissor != state->scissorBox) { mScissor = state->scissorBox; mLocalDirtyBits.set(gl::State::DIRTY_BIT_SCISSOR); } get(GL_DEPTH_TEST, &state->depthTest); if (mDepthTestEnabled != state->depthTest) { mDepthTestEnabled = state->depthTest; mLocalDirtyBits.set(gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED); } get(GL_CULL_FACE, &state->cullFace); if (mCullFaceEnabled != state->cullFace) { mCullFaceEnabled = state->cullFace; mLocalDirtyBits.set(gl::State::DIRTY_BIT_CULL_FACE_ENABLED); } get(GL_CULL_FACE_MODE, &state->cullFaceMode); if (mCullFace != gl::FromGLenum(state->cullFaceMode)) { mCullFace = gl::FromGLenum(state->cullFaceMode); mLocalDirtyBits.set(gl::State::DIRTY_BIT_CULL_FACE); } get(GL_COLOR_WRITEMASK, &state->colorMask); auto colorMask = mBlendStateExt.expandColorMaskValue(state->colorMask[0], state->colorMask[1], state->colorMask[2], state->colorMask[3]); if (mBlendStateExt.mColorMask != colorMask) { mBlendStateExt.mColorMask = colorMask; mLocalDirtyBits.set(gl::State::DIRTY_BIT_COLOR_MASK); } get(GL_CURRENT_PROGRAM, &state->currentProgram); if (mProgram != static_cast(state->currentProgram)) { mProgram = state->currentProgram; mLocalDirtyBits.set(gl::State::DIRTY_BIT_PROGRAM_BINDING); } get(GL_COLOR_CLEAR_VALUE, &state->colorClear); if (mClearColor != state->colorClear) { mClearColor = state->colorClear; mLocalDirtyBits.set(gl::State::DIRTY_BIT_CLEAR_COLOR); } get(GL_DEPTH_CLEAR_VALUE, &state->depthClear); if (mClearDepth != state->depthClear) { mClearDepth = state->depthClear; mLocalDirtyBits.set(gl::State::DIRTY_BIT_CLEAR_DEPTH); } get(GL_DEPTH_FUNC, &state->depthFunc); if (mDepthFunc != static_cast(state->depthFunc)) { mDepthFunc = state->depthFunc; mLocalDirtyBits.set(gl::State::DIRTY_BIT_DEPTH_FUNC); } get(GL_DEPTH_WRITEMASK, &state->depthMask); if (mDepthMask != state->depthMask) { mDepthMask = state->depthMask; mLocalDirtyBits.set(gl::State::DIRTY_BIT_DEPTH_MASK); } get(GL_DEPTH_RANGE, state->depthRage); if (mNear != state->depthRage[0] || mFar != state->depthRage[1]) { mNear = state->depthRage[0]; mFar = state->depthRage[1]; mLocalDirtyBits.set(gl::State::DIRTY_BIT_DEPTH_RANGE); } get(GL_FRONT_FACE, &state->frontFace); if (mFrontFace != static_cast(state->frontFace)) { mFrontFace = state->frontFace; mLocalDirtyBits.set(gl::State::DIRTY_BIT_FRONT_FACE); } get(GL_LINE_WIDTH, &state->lineWidth); if (mLineWidth != state->lineWidth) { mLineWidth = state->lineWidth; mLocalDirtyBits.set(gl::State::DIRTY_BIT_LINE_WIDTH); } get(GL_POLYGON_OFFSET_FACTOR, &state->polygonOffsetFactor); get(GL_POLYGON_OFFSET_UNITS, &state->polygonOffsetUnits); if (mPolygonOffsetFactor != state->polygonOffsetFactor || mPolygonOffsetUnits != state->polygonOffsetUnits) { mPolygonOffsetFactor = state->polygonOffsetFactor; mPolygonOffsetUnits = state->polygonOffsetUnits; mLocalDirtyBits.set(gl::State::DIRTY_BIT_POLYGON_OFFSET); } get(GL_SAMPLE_COVERAGE_VALUE, &state->sampleCoverageValue); get(GL_SAMPLE_COVERAGE_INVERT, &state->sampleCoverageInvert); if (mSampleCoverageValue != state->sampleCoverageValue || mSampleCoverageInvert != state->sampleCoverageInvert) { mSampleCoverageValue = state->sampleCoverageValue; mSampleCoverageInvert = state->sampleCoverageInvert; mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_COVERAGE); } get(GL_DITHER, &state->enableDither); if (mDitherEnabled != state->enableDither) { mDitherEnabled = state->enableDither; mLocalDirtyBits.set(gl::State::DIRTY_BIT_DITHER_ENABLED); } get(GL_POLYGON_OFFSET_FILL, &state->enablePolygonOffsetFill); if (mPolygonOffsetFillEnabled != state->enablePolygonOffsetFill) { mPolygonOffsetFillEnabled = state->enablePolygonOffsetFill; mLocalDirtyBits.set(gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED); } get(GL_SAMPLE_ALPHA_TO_COVERAGE, &state->enableSampleAlphaToCoverage); if (mSampleAlphaToOneEnabled != state->enableSampleAlphaToCoverage) { mSampleAlphaToOneEnabled = state->enableSampleAlphaToCoverage; mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE); } get(GL_SAMPLE_COVERAGE, &state->enableSampleCoverage); if (mSampleCoverageEnabled != state->enableSampleCoverage) { mSampleCoverageEnabled = state->enableSampleCoverage; mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED); } if (extensions.multisampleCompatibility) { get(GL_MULTISAMPLE, &state->multisampleEnabled); if (mMultisamplingEnabled != state->multisampleEnabled) { mMultisamplingEnabled = state->multisampleEnabled; mLocalDirtyBits.set(gl::State::DIRTY_BIT_MULTISAMPLING); } } syncBlendFromNativeContext(extensions, state); syncFramebufferFromNativeContext(extensions, state); syncPixelPackUnpackFromNativeContext(extensions, state); syncStencilFromNativeContext(extensions, state); syncVertexArraysFromNativeContext(extensions, state); syncBufferBindingsFromNativeContext(extensions, state); syncTextureUnitsFromNativeContext(extensions, state); ASSERT(mFunctions->getError() == GL_NO_ERROR); } void StateManagerGL::restoreNativeContext(const gl::Extensions &extensions, const ExternalContextState *state) { ASSERT(mFunctions->getError() == GL_NO_ERROR); setViewport(state->viewport); setScissorTestEnabled(state->scissorTest); setScissor(state->scissorBox); setDepthTestEnabled(state->depthTest); setCullFaceEnabled(state->cullFace); setCullFace(gl::FromGLenum(state->cullFaceMode)); setColorMask(state->colorMask[0], state->colorMask[1], state->colorMask[2], state->colorMask[3]); forceUseProgram(state->currentProgram); setClearColor(state->colorClear); setClearDepth(state->depthClear); setDepthFunc(state->depthFunc); setDepthMask(state->depthMask); setDepthRange(state->depthRage[0], state->depthRage[1]); setFrontFace(state->frontFace); setLineWidth(state->lineWidth); setPolygonOffset(state->polygonOffsetFactor, state->polygonOffsetUnits); setSampleCoverage(state->sampleCoverageValue, state->sampleCoverageInvert); setDitherEnabled(state->enableDither); setPolygonOffsetFillEnabled(state->enablePolygonOffsetFill); setSampleAlphaToOneStateEnabled(state->enableSampleAlphaToCoverage); setSampleCoverageEnabled(state->enableSampleCoverage); if (extensions.multisampleCompatibility) setMultisamplingStateEnabled(state->multisampleEnabled); restoreBlendNativeContext(extensions, state); restoreFramebufferNativeContext(extensions, state); restorePixelPackUnpackNativeContext(extensions, state); restoreStencilNativeContext(extensions, state); restoreVertexArraysNativeContext(extensions, state); restoreBufferBindingsNativeContext(extensions, state); restoreTextureUnitsNativeContext(extensions, state); // if (mFunctions->coverageModulationNV) ? setCoverageModulation(GL_NONE); ASSERT(mFunctions->getError() == GL_NO_ERROR); } void StateManagerGL::syncBlendFromNativeContext(const gl::Extensions &extensions, ExternalContextState *state) { get(GL_BLEND, &state->blendEnabled); if (mBlendStateExt.mEnabledMask != (state->blendEnabled ? mBlendStateExt.mMaxEnabledMask : gl::DrawBufferMask::Zero())) { mBlendStateExt.setEnabled(state->blendEnabled); mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_ENABLED); } get(GL_BLEND_SRC_RGB, &state->blendSrcRgb); get(GL_BLEND_DST_RGB, &state->blendDestRgb); get(GL_BLEND_SRC_ALPHA, &state->blendSrcAlpha); get(GL_BLEND_DST_ALPHA, &state->blendDestAlpha); if (mBlendStateExt.mSrcColor != static_cast(state->blendSrcRgb) || mBlendStateExt.mDstColor != static_cast(state->blendDestRgb) || mBlendStateExt.mSrcAlpha != static_cast(state->blendSrcAlpha) || mBlendStateExt.mDstAlpha != static_cast(state->blendDestAlpha)) { mBlendStateExt.mSrcColor = state->blendSrcRgb; mBlendStateExt.mDstColor = state->blendDestRgb; mBlendStateExt.mSrcAlpha = state->blendSrcAlpha; mBlendStateExt.mDstAlpha = state->blendDestAlpha; mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_FUNCS); } get(GL_BLEND_COLOR, &state->blendColor); if (mBlendColor != state->blendColor) { mBlendColor = state->blendColor; mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_COLOR); } get(GL_BLEND_EQUATION_RGB, &state->blendEquationRgb); get(GL_BLEND_EQUATION_ALPHA, &state->blendEquationAlpha); } void StateManagerGL::restoreBlendNativeContext(const gl::Extensions &extensions, const ExternalContextState *state) { setBlendEnabled(state->blendEnabled); // TODO: use setBlendFuncs() mFunctions->blendFuncSeparate(state->blendSrcRgb, state->blendDestRgb, state->blendSrcAlpha, state->blendDestAlpha); mBlendStateExt.mSrcColor = state->blendSrcRgb; mBlendStateExt.mDstColor = state->blendDestRgb; mBlendStateExt.mSrcAlpha = state->blendSrcAlpha; mBlendStateExt.mDstAlpha = state->blendDestAlpha; mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_FUNCS); setBlendColor(state->blendColor); // TODO: use setBlendEquations() mFunctions->blendEquationSeparate(state->blendEquationRgb, state->blendEquationAlpha); mBlendStateExt.mEquationColor = state->blendEquationRgb; mBlendStateExt.mEquationAlpha = state->blendEquationAlpha; mLocalDirtyBits.set(gl::State::DIRTY_BIT_BLEND_EQUATIONS); } void StateManagerGL::syncFramebufferFromNativeContext(const gl::Extensions &extensions, ExternalContextState *state) { // TODO: wrap fbo into an EGLSurface get(GL_FRAMEBUFFER_BINDING, &state->framebufferBinding); if (mFramebuffers[angle::FramebufferBindingDraw] != static_cast(state->framebufferBinding)) { mFramebuffers[angle::FramebufferBindingDraw] = static_cast(state->framebufferBinding); mLocalDirtyBits.set(gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING); } if (mFramebuffers[angle::FramebufferBindingRead] != static_cast(state->framebufferBinding)) { mFramebuffers[angle::FramebufferBindingRead] = static_cast(state->framebufferBinding); mLocalDirtyBits.set(gl::State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING); } } void StateManagerGL::restoreFramebufferNativeContext(const gl::Extensions &extensions, const ExternalContextState *state) { bindFramebuffer(GL_FRAMEBUFFER, state->framebufferBinding); } void StateManagerGL::syncPixelPackUnpackFromNativeContext(const gl::Extensions &extensions, ExternalContextState *state) { get(GL_PACK_ALIGNMENT, &state->packAlignment); if (mPackAlignment != state->packAlignment) { mPackAlignment = state->packAlignment; mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE); } get(GL_UNPACK_ALIGNMENT, &state->unpackAlignment); if (mUnpackAlignment != state->unpackAlignment) { mUnpackAlignment = state->unpackAlignment; mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE); } } void StateManagerGL::restorePixelPackUnpackNativeContext(const gl::Extensions &extensions, const ExternalContextState *state) { if (mPackAlignment != state->packAlignment) { mFunctions->pixelStorei(GL_PACK_ALIGNMENT, state->packAlignment); mPackAlignment = state->packAlignment; mLocalDirtyBits.set(gl::State::DIRTY_BIT_PACK_STATE); } if (mUnpackAlignment != state->unpackAlignment) { mFunctions->pixelStorei(GL_UNPACK_ALIGNMENT, state->unpackAlignment); mUnpackAlignment = state->unpackAlignment; mLocalDirtyBits.set(gl::State::DIRTY_BIT_UNPACK_STATE); } } void StateManagerGL::syncStencilFromNativeContext(const gl::Extensions &extensions, ExternalContextState *state) { get(GL_STENCIL_TEST, &state->stencilState.stencilTestEnabled); if (state->stencilState.stencilTestEnabled != mStencilTestEnabled) { mStencilTestEnabled = state->stencilState.stencilTestEnabled; mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED); } get(GL_STENCIL_FUNC, &state->stencilState.stencilFrontFunc); get(GL_STENCIL_VALUE_MASK, &state->stencilState.stencilFrontMask); get(GL_STENCIL_REF, &state->stencilState.stencilFrontRef); if (state->stencilState.stencilFrontFunc != mStencilFrontFunc || state->stencilState.stencilFrontMask != mStencilFrontValueMask || state->stencilState.stencilFrontRef != mStencilFrontRef) { mStencilFrontFunc = state->stencilState.stencilFrontFunc; mStencilFrontValueMask = state->stencilState.stencilFrontMask; mStencilFrontRef = state->stencilState.stencilFrontRef; mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT); } get(GL_STENCIL_BACK_FUNC, &state->stencilState.stencilBackFunc); get(GL_STENCIL_BACK_VALUE_MASK, &state->stencilState.stencilBackMask); get(GL_STENCIL_BACK_REF, &state->stencilState.stencilBackRef); if (state->stencilState.stencilBackFunc != mStencilBackFunc || state->stencilState.stencilBackMask != mStencilBackValueMask || state->stencilState.stencilBackRef != mStencilBackRef) { mStencilBackFunc = state->stencilState.stencilBackFunc; mStencilBackValueMask = state->stencilState.stencilBackMask; mStencilBackRef = state->stencilState.stencilBackRef; mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK); } get(GL_STENCIL_CLEAR_VALUE, &state->stencilState.stencilClear); if (mClearStencil != state->stencilState.stencilClear) { mClearStencil = state->stencilState.stencilClear; mLocalDirtyBits.set(gl::State::DIRTY_BIT_CLEAR_STENCIL); } get(GL_STENCIL_WRITEMASK, &state->stencilState.stencilFrontWritemask); if (mStencilFrontWritemask != static_cast(state->stencilState.stencilFrontWritemask)) { mStencilFrontWritemask = state->stencilState.stencilFrontWritemask; mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT); } get(GL_STENCIL_BACK_WRITEMASK, &state->stencilState.stencilBackWritemask); if (mStencilBackWritemask != static_cast(state->stencilState.stencilBackWritemask)) { mStencilBackWritemask = state->stencilState.stencilBackWritemask; mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT); } get(GL_STENCIL_FAIL, &state->stencilState.stencilFrontFailOp); get(GL_STENCIL_PASS_DEPTH_FAIL, &state->stencilState.stencilFrontZFailOp); get(GL_STENCIL_PASS_DEPTH_PASS, &state->stencilState.stencilFrontZPassOp); if (mStencilFrontStencilFailOp != static_cast(state->stencilState.stencilFrontFailOp) || mStencilFrontStencilPassDepthFailOp != static_cast(state->stencilState.stencilFrontZFailOp) || mStencilFrontStencilPassDepthPassOp != static_cast(state->stencilState.stencilFrontZPassOp)) { mStencilFrontStencilFailOp = static_cast(state->stencilState.stencilFrontFailOp); mStencilFrontStencilPassDepthFailOp = static_cast(state->stencilState.stencilFrontZFailOp); mStencilFrontStencilPassDepthPassOp = static_cast(state->stencilState.stencilFrontZPassOp); mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_OPS_FRONT); } get(GL_STENCIL_BACK_FAIL, &state->stencilState.stencilBackFailOp); get(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &state->stencilState.stencilBackZFailOp); get(GL_STENCIL_BACK_PASS_DEPTH_PASS, &state->stencilState.stencilBackZPassOp); if (mStencilBackStencilFailOp != static_cast(state->stencilState.stencilBackFailOp) || mStencilBackStencilPassDepthFailOp != static_cast(state->stencilState.stencilBackZFailOp) || mStencilBackStencilPassDepthPassOp != static_cast(state->stencilState.stencilBackZPassOp)) { mStencilBackStencilFailOp = static_cast(state->stencilState.stencilBackFailOp); mStencilBackStencilPassDepthFailOp = static_cast(state->stencilState.stencilBackZFailOp); mStencilBackStencilPassDepthPassOp = static_cast(state->stencilState.stencilBackZPassOp); mLocalDirtyBits.set(gl::State::DIRTY_BIT_STENCIL_OPS_BACK); } } void StateManagerGL::restoreStencilNativeContext(const gl::Extensions &extensions, const ExternalContextState *state) { setStencilTestEnabled(state->stencilState.stencilTestEnabled); setStencilFrontFuncs(state->stencilState.stencilFrontFunc, state->stencilState.stencilFrontMask, state->stencilState.stencilFrontRef); setStencilBackFuncs(state->stencilState.stencilBackFunc, state->stencilState.stencilBackMask, state->stencilState.stencilBackRef); setClearStencil(state->stencilState.stencilClear); setStencilFrontWritemask(state->stencilState.stencilFrontWritemask); setStencilBackWritemask(state->stencilState.stencilBackWritemask); setStencilFrontOps(state->stencilState.stencilFrontFailOp, state->stencilState.stencilFrontZFailOp, state->stencilState.stencilFrontZPassOp); setStencilBackOps(state->stencilState.stencilBackFailOp, state->stencilState.stencilBackZFailOp, state->stencilState.stencilBackZPassOp); } void StateManagerGL::syncBufferBindingsFromNativeContext(const gl::Extensions &extensions, ExternalContextState *state) { get(GL_ARRAY_BUFFER_BINDING, &state->vertexArrayBufferBinding); mBuffers[gl::BufferBinding::Array] = state->vertexArrayBufferBinding; get(GL_ELEMENT_ARRAY_BUFFER_BINDING, &state->elementArrayBufferBinding); mBuffers[gl::BufferBinding::ElementArray] = state->elementArrayBufferBinding; } void StateManagerGL::restoreBufferBindingsNativeContext(const gl::Extensions &extensions, const ExternalContextState *state) { bindBuffer(gl::BufferBinding::Array, state->vertexArrayBufferBinding); bindBuffer(gl::BufferBinding::ElementArray, state->elementArrayBufferBinding); } void StateManagerGL::syncTextureUnitsFromNativeContext(const gl::Extensions &extensions, ExternalContextState *state) { get(GL_ACTIVE_TEXTURE, &state->activeTexture); for (size_t i = 0; i < state->textureBindings.size(); ++i) { auto &bindings = state->textureBindings[i]; activeTexture(i); get(GL_TEXTURE_BINDING_2D, &bindings.texture2d); get(GL_TEXTURE_BINDING_CUBE_MAP, &bindings.textureCubeMap); get(GL_TEXTURE_BINDING_EXTERNAL_OES, &bindings.textureExternalOES); if (mTextures[gl::TextureType::_2D][i] != static_cast(bindings.texture2d) || mTextures[gl::TextureType::CubeMap][i] != static_cast(bindings.textureCubeMap) || mTextures[gl::TextureType::External][i] != static_cast(bindings.textureExternalOES)) { mTextures[gl::TextureType::_2D][i] = bindings.texture2d; mTextures[gl::TextureType::CubeMap][i] = bindings.textureCubeMap; mTextures[gl::TextureType::External][i] = bindings.textureExternalOES; mLocalDirtyBits.set(gl::State::DIRTY_BIT_TEXTURE_BINDINGS); } } } void StateManagerGL::restoreTextureUnitsNativeContext(const gl::Extensions &extensions, const ExternalContextState *state) { for (size_t i = 0; i < state->textureBindings.size(); ++i) { const auto &bindings = state->textureBindings[i]; activeTexture(i); bindTexture(gl::TextureType::_2D, bindings.texture2d); bindTexture(gl::TextureType::CubeMap, bindings.textureCubeMap); bindTexture(gl::TextureType::External, bindings.textureExternalOES); bindSampler(i, 0); } activeTexture(state->activeTexture - GL_TEXTURE0); } void StateManagerGL::syncVertexArraysFromNativeContext(const gl::Extensions &extensions, ExternalContextState *state) { get(GL_VERTEX_ARRAY_BINDING, &state->vertexArrayBinding); if (mVAO != static_cast(state->vertexArrayBinding)) { mVAO = state->vertexArrayBinding; mBuffers[gl::BufferBinding::ElementArray] = 0; mLocalDirtyBits.set(gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING); } } void StateManagerGL::restoreVertexArraysNativeContext(const gl::Extensions &extensions, const ExternalContextState *state) { bindVertexArray(state->vertexArrayBinding, 0); } void StateManagerGL::setDefaultVAOStateDirty() { mLocalDirtyBits.set(gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING); } } // namespace rx