| // | |
| // Copyright (c) 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 <limits> | |
| #include <string.h> | |
| #include "common/bitset_utils.h" | |
| #include "common/mathutil.h" | |
| #include "common/matrix_utils.h" | |
| #include "libANGLE/Caps.h" | |
| #include "libANGLE/Context.h" | |
| #include "libANGLE/Debug.h" | |
| #include "libANGLE/Framebuffer.h" | |
| #include "libANGLE/FramebufferAttachment.h" | |
| #include "libANGLE/Query.h" | |
| #include "libANGLE/VertexArray.h" | |
| #include "libANGLE/formatutils.h" | |
| #include "libANGLE/queryconversions.h" | |
| #include "libANGLE/renderer/ContextImpl.h" | |
| namespace | |
| { | |
| GLenum ActiveQueryType(const GLenum type) | |
| { | |
| return (type == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) ? GL_ANY_SAMPLES_PASSED : type; | |
| } | |
| } // anonymous namepace | |
| namespace gl | |
| { | |
| State::State() | |
| : mMaxDrawBuffers(0), | |
| mMaxCombinedTextureImageUnits(0), | |
| mDepthClearValue(0), | |
| mStencilClearValue(0), | |
| mScissorTest(false), | |
| mSampleCoverage(false), | |
| mSampleCoverageValue(0), | |
| mSampleCoverageInvert(false), | |
| mSampleMask(false), | |
| mMaxSampleMaskWords(0), | |
| mStencilRef(0), | |
| mStencilBackRef(0), | |
| mLineWidth(0), | |
| mGenerateMipmapHint(GL_NONE), | |
| mFragmentShaderDerivativeHint(GL_NONE), | |
| mBindGeneratesResource(true), | |
| mClientArraysEnabled(true), | |
| mNearZ(0), | |
| mFarZ(0), | |
| mReadFramebuffer(nullptr), | |
| mDrawFramebuffer(nullptr), | |
| mProgram(nullptr), | |
| mVertexArray(nullptr), | |
| mActiveSampler(0), | |
| mPrimitiveRestart(false), | |
| mMultiSampling(false), | |
| mSampleAlphaToOne(false), | |
| mFramebufferSRGB(true), | |
| mRobustResourceInit(false), | |
| mProgramBinaryCacheEnabled(false) | |
| { | |
| } | |
| State::~State() | |
| { | |
| } | |
| void State::initialize(const Context *context, | |
| bool debug, | |
| bool bindGeneratesResource, | |
| bool clientArraysEnabled, | |
| bool robustResourceInit, | |
| bool programBinaryCacheEnabled) | |
| { | |
| const Caps &caps = context->getCaps(); | |
| const Extensions &extensions = context->getExtensions(); | |
| const Extensions &nativeExtensions = context->getImplementation()->getNativeExtensions(); | |
| const Version &clientVersion = context->getClientVersion(); | |
| mMaxDrawBuffers = caps.maxDrawBuffers; | |
| mMaxCombinedTextureImageUnits = caps.maxCombinedTextureImageUnits; | |
| 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 = caps.maxSampleMaskWords; | |
| mSampleMask = false; | |
| mSampleMaskValues.fill(~GLbitfield(0)); | |
| mGenerateMipmapHint = GL_DONT_CARE; | |
| mFragmentShaderDerivativeHint = GL_DONT_CARE; | |
| mBindGeneratesResource = bindGeneratesResource; | |
| mClientArraysEnabled = clientArraysEnabled; | |
| mLineWidth = 1.0f; | |
| mViewport.x = 0; | |
| mViewport.y = 0; | |
| mViewport.width = 0; | |
| mViewport.height = 0; | |
| mNearZ = 0.0f; | |
| mFarZ = 1.0f; | |
| mBlend.colorMaskRed = true; | |
| mBlend.colorMaskGreen = true; | |
| mBlend.colorMaskBlue = true; | |
| mBlend.colorMaskAlpha = true; | |
| mActiveSampler = 0; | |
| mVertexAttribCurrentValues.resize(caps.maxVertexAttributes); | |
| // Set all indexes in state attributes type mask to float (default) | |
| for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++) | |
| { | |
| mCurrentValuesTypeMask.setIndex(GL_FLOAT, i); | |
| } | |
| mUniformBuffers.resize(caps.maxUniformBufferBindings); | |
| mSamplerTextures[TextureType::_2D].resize(caps.maxCombinedTextureImageUnits); | |
| mSamplerTextures[TextureType::CubeMap].resize(caps.maxCombinedTextureImageUnits); | |
| if (clientVersion >= Version(3, 0)) | |
| { | |
| // TODO: These could also be enabled via extension | |
| mSamplerTextures[TextureType::_2DArray].resize(caps.maxCombinedTextureImageUnits); | |
| mSamplerTextures[TextureType::_3D].resize(caps.maxCombinedTextureImageUnits); | |
| } | |
| if (clientVersion >= Version(3, 1)) | |
| { | |
| mSamplerTextures[TextureType::_2DMultisample].resize(caps.maxCombinedTextureImageUnits); | |
| mAtomicCounterBuffers.resize(caps.maxAtomicCounterBufferBindings); | |
| mShaderStorageBuffers.resize(caps.maxShaderStorageBufferBindings); | |
| mImageUnits.resize(caps.maxImageUnits); | |
| } | |
| if (nativeExtensions.textureRectangle) | |
| { | |
| mSamplerTextures[TextureType::Rectangle].resize(caps.maxCombinedTextureImageUnits); | |
| } | |
| if (nativeExtensions.eglImageExternal || nativeExtensions.eglStreamConsumerExternal) | |
| { | |
| mSamplerTextures[TextureType::External].resize(caps.maxCombinedTextureImageUnits); | |
| } | |
| mCompleteTextureCache.resize(caps.maxCombinedTextureImageUnits, nullptr); | |
| mCompleteTextureBindings.reserve(caps.maxCombinedTextureImageUnits); | |
| mCachedTexturesInitState = InitState::MayNeedInit; | |
| for (uint32_t textureIndex = 0; textureIndex < caps.maxCombinedTextureImageUnits; | |
| ++textureIndex) | |
| { | |
| mCompleteTextureBindings.emplace_back(this, textureIndex); | |
| } | |
| mSamplers.resize(caps.maxCombinedTextureImageUnits); | |
| mActiveQueries[GL_ANY_SAMPLES_PASSED].set(context, nullptr); | |
| mActiveQueries[GL_ANY_SAMPLES_PASSED_CONSERVATIVE].set(context, nullptr); | |
| mActiveQueries[GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN].set(context, nullptr); | |
| mActiveQueries[GL_TIME_ELAPSED_EXT].set(context, nullptr); | |
| mActiveQueries[GL_COMMANDS_COMPLETED_CHROMIUM].set(context, nullptr); | |
| mProgram = nullptr; | |
| mReadFramebuffer = nullptr; | |
| mDrawFramebuffer = nullptr; | |
| mPrimitiveRestart = false; | |
| mDebug.setOutputEnabled(debug); | |
| mDebug.setMaxLoggedMessages(extensions.maxDebugLoggedMessages); | |
| mMultiSampling = true; | |
| mSampleAlphaToOne = false; | |
| mCoverageModulation = GL_NONE; | |
| angle::Matrix<GLfloat>::setToIdentity(mPathMatrixProj); | |
| angle::Matrix<GLfloat>::setToIdentity(mPathMatrixMV); | |
| mPathStencilFunc = GL_ALWAYS; | |
| mPathStencilRef = 0; | |
| mPathStencilMask = std::numeric_limits<GLuint>::max(); | |
| mRobustResourceInit = robustResourceInit; | |
| mProgramBinaryCacheEnabled = programBinaryCacheEnabled; | |
| } | |
| void State::reset(const Context *context) | |
| { | |
| for (auto &bindingVec : mSamplerTextures) | |
| { | |
| for (size_t textureIdx = 0; textureIdx < bindingVec.size(); textureIdx++) | |
| { | |
| bindingVec[textureIdx].set(context, nullptr); | |
| } | |
| } | |
| for (size_t samplerIdx = 0; samplerIdx < mSamplers.size(); samplerIdx++) | |
| { | |
| mSamplers[samplerIdx].set(context, nullptr); | |
| } | |
| for (auto &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 (auto type : angle::AllEnums<BufferBinding>()) | |
| { | |
| mBoundBuffers[type].set(context, nullptr); | |
| } | |
| if (mProgram) | |
| { | |
| mProgram->release(context); | |
| } | |
| mProgram = nullptr; | |
| mProgramPipeline.set(context, nullptr); | |
| mTransformFeedback.set(context, nullptr); | |
| for (State::ActiveQueryMap::iterator i = mActiveQueries.begin(); i != mActiveQueries.end(); i++) | |
| { | |
| i->second.set(context, nullptr); | |
| } | |
| for (auto &buf : mUniformBuffers) | |
| { | |
| buf.set(context, nullptr); | |
| } | |
| for (auto &buf : mAtomicCounterBuffers) | |
| { | |
| buf.set(context, nullptr); | |
| } | |
| for (auto &buf : mShaderStorageBuffers) | |
| { | |
| buf.set(context, nullptr); | |
| } | |
| angle::Matrix<GLfloat>::setToIdentity(mPathMatrixProj); | |
| angle::Matrix<GLfloat>::setToIdentity(mPathMatrixMV); | |
| mPathStencilFunc = GL_ALWAYS; | |
| mPathStencilRef = 0; | |
| mPathStencilMask = std::numeric_limits<GLuint>::max(); | |
| // TODO(jmadill): Is this necessary? | |
| setAllDirtyBits(); | |
| } | |
| const RasterizerState &State::getRasterizerState() const | |
| { | |
| return mRasterizer; | |
| } | |
| const BlendState &State::getBlendState() const | |
| { | |
| return mBlend; | |
| } | |
| 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) | |
| { | |
| mBlend.colorMaskRed = red; | |
| mBlend.colorMaskGreen = green; | |
| mBlend.colorMaskBlue = blue; | |
| mBlend.colorMaskAlpha = alpha; | |
| mDirtyBits.set(DIRTY_BIT_COLOR_MASK); | |
| } | |
| void State::setDepthMask(bool mask) | |
| { | |
| mDepthStencil.depthMask = mask; | |
| mDirtyBits.set(DIRTY_BIT_DEPTH_MASK); | |
| } | |
| bool State::isRasterizerDiscardEnabled() const | |
| { | |
| return mRasterizer.rasterizerDiscard; | |
| } | |
| void State::setRasterizerDiscard(bool enabled) | |
| { | |
| mRasterizer.rasterizerDiscard = enabled; | |
| mDirtyBits.set(DIRTY_BIT_RASTERIZER_DISCARD_ENABLED); | |
| } | |
| bool State::isCullFaceEnabled() const | |
| { | |
| return mRasterizer.cullFace; | |
| } | |
| void State::setCullFace(bool enabled) | |
| { | |
| mRasterizer.cullFace = enabled; | |
| mDirtyBits.set(DIRTY_BIT_CULL_FACE_ENABLED); | |
| } | |
| void State::setCullMode(CullFaceMode mode) | |
| { | |
| mRasterizer.cullMode = mode; | |
| mDirtyBits.set(DIRTY_BIT_CULL_FACE); | |
| } | |
| void State::setFrontFace(GLenum front) | |
| { | |
| mRasterizer.frontFace = front; | |
| mDirtyBits.set(DIRTY_BIT_FRONT_FACE); | |
| } | |
| bool State::isDepthTestEnabled() const | |
| { | |
| return mDepthStencil.depthTest; | |
| } | |
| void State::setDepthTest(bool enabled) | |
| { | |
| mDepthStencil.depthTest = enabled; | |
| mDirtyBits.set(DIRTY_BIT_DEPTH_TEST_ENABLED); | |
| } | |
| void State::setDepthFunc(GLenum depthFunc) | |
| { | |
| mDepthStencil.depthFunc = depthFunc; | |
| mDirtyBits.set(DIRTY_BIT_DEPTH_FUNC); | |
| } | |
| void State::setDepthRange(float zNear, float zFar) | |
| { | |
| mNearZ = zNear; | |
| mFarZ = zFar; | |
| mDirtyBits.set(DIRTY_BIT_DEPTH_RANGE); | |
| } | |
| float State::getNearPlane() const | |
| { | |
| return mNearZ; | |
| } | |
| float State::getFarPlane() const | |
| { | |
| return mFarZ; | |
| } | |
| bool State::isBlendEnabled() const | |
| { | |
| return mBlend.blend; | |
| } | |
| void State::setBlend(bool enabled) | |
| { | |
| mBlend.blend = enabled; | |
| mDirtyBits.set(DIRTY_BIT_BLEND_ENABLED); | |
| } | |
| void State::setBlendFactors(GLenum sourceRGB, GLenum destRGB, GLenum sourceAlpha, GLenum destAlpha) | |
| { | |
| mBlend.sourceBlendRGB = sourceRGB; | |
| mBlend.destBlendRGB = destRGB; | |
| mBlend.sourceBlendAlpha = sourceAlpha; | |
| mBlend.destBlendAlpha = destAlpha; | |
| mDirtyBits.set(DIRTY_BIT_BLEND_FUNCS); | |
| } | |
| void State::setBlendColor(float red, float green, float blue, float 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) | |
| { | |
| mBlend.blendEquationRGB = rgbEquation; | |
| mBlend.blendEquationAlpha = alphaEquation; | |
| mDirtyBits.set(DIRTY_BIT_BLEND_EQUATIONS); | |
| } | |
| const ColorF &State::getBlendColor() const | |
| { | |
| return mBlendColor; | |
| } | |
| bool State::isStencilTestEnabled() const | |
| { | |
| return mDepthStencil.stencilTest; | |
| } | |
| void State::setStencilTest(bool enabled) | |
| { | |
| mDepthStencil.stencilTest = enabled; | |
| mDirtyBits.set(DIRTY_BIT_STENCIL_TEST_ENABLED); | |
| } | |
| void State::setStencilParams(GLenum stencilFunc, GLint stencilRef, GLuint stencilMask) | |
| { | |
| mDepthStencil.stencilFunc = stencilFunc; | |
| mStencilRef = (stencilRef > 0) ? stencilRef : 0; | |
| mDepthStencil.stencilMask = stencilMask; | |
| mDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_FRONT); | |
| } | |
| void State::setStencilBackParams(GLenum stencilBackFunc, GLint stencilBackRef, GLuint stencilBackMask) | |
| { | |
| mDepthStencil.stencilBackFunc = stencilBackFunc; | |
| mStencilBackRef = (stencilBackRef > 0) ? stencilBackRef : 0; | |
| mDepthStencil.stencilBackMask = stencilBackMask; | |
| mDirtyBits.set(DIRTY_BIT_STENCIL_FUNCS_BACK); | |
| } | |
| void State::setStencilWritemask(GLuint stencilWritemask) | |
| { | |
| mDepthStencil.stencilWritemask = stencilWritemask; | |
| mDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_FRONT); | |
| } | |
| void State::setStencilBackWritemask(GLuint stencilBackWritemask) | |
| { | |
| mDepthStencil.stencilBackWritemask = stencilBackWritemask; | |
| mDirtyBits.set(DIRTY_BIT_STENCIL_WRITEMASK_BACK); | |
| } | |
| void State::setStencilOperations(GLenum stencilFail, GLenum stencilPassDepthFail, GLenum 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) | |
| { | |
| mDepthStencil.stencilBackFail = stencilBackFail; | |
| mDepthStencil.stencilBackPassDepthFail = stencilBackPassDepthFail; | |
| mDepthStencil.stencilBackPassDepthPass = stencilBackPassDepthPass; | |
| mDirtyBits.set(DIRTY_BIT_STENCIL_OPS_BACK); | |
| } | |
| GLint State::getStencilRef() const | |
| { | |
| return mStencilRef; | |
| } | |
| GLint State::getStencilBackRef() const | |
| { | |
| return mStencilBackRef; | |
| } | |
| bool State::isPolygonOffsetFillEnabled() const | |
| { | |
| return mRasterizer.polygonOffsetFill; | |
| } | |
| void State::setPolygonOffsetFill(bool 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); | |
| } | |
| bool State::isSampleAlphaToCoverageEnabled() const | |
| { | |
| return mBlend.sampleAlphaToCoverage; | |
| } | |
| void State::setSampleAlphaToCoverage(bool enabled) | |
| { | |
| mBlend.sampleAlphaToCoverage = enabled; | |
| mDirtyBits.set(DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED); | |
| } | |
| bool State::isSampleCoverageEnabled() const | |
| { | |
| return mSampleCoverage; | |
| } | |
| void State::setSampleCoverage(bool 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); | |
| } | |
| GLclampf State::getSampleCoverageValue() const | |
| { | |
| return mSampleCoverageValue; | |
| } | |
| bool State::getSampleCoverageInvert() const | |
| { | |
| return mSampleCoverageInvert; | |
| } | |
| bool State::isSampleMaskEnabled() const | |
| { | |
| return mSampleMask; | |
| } | |
| void State::setSampleMaskEnabled(bool 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); | |
| } | |
| GLbitfield State::getSampleMaskWord(GLuint maskNumber) const | |
| { | |
| ASSERT(maskNumber < mMaxSampleMaskWords); | |
| return mSampleMaskValues[maskNumber]; | |
| } | |
| GLuint State::getMaxSampleMaskWords() const | |
| { | |
| return mMaxSampleMaskWords; | |
| } | |
| void State::setSampleAlphaToOne(bool enabled) | |
| { | |
| mSampleAlphaToOne = enabled; | |
| mDirtyBits.set(DIRTY_BIT_SAMPLE_ALPHA_TO_ONE); | |
| } | |
| bool State::isSampleAlphaToOneEnabled() const | |
| { | |
| return mSampleAlphaToOne; | |
| } | |
| void State::setMultisampling(bool enabled) | |
| { | |
| mMultiSampling = enabled; | |
| mDirtyBits.set(DIRTY_BIT_MULTISAMPLING); | |
| } | |
| bool State::isMultisamplingEnabled() const | |
| { | |
| return mMultiSampling; | |
| } | |
| bool State::isScissorTestEnabled() const | |
| { | |
| return mScissorTest; | |
| } | |
| void State::setScissorTest(bool enabled) | |
| { | |
| mScissorTest = enabled; | |
| mDirtyBits.set(DIRTY_BIT_SCISSOR_TEST_ENABLED); | |
| } | |
| void State::setScissorParams(GLint x, GLint y, GLsizei width, GLsizei height) | |
| { | |
| mScissor.x = x; | |
| mScissor.y = y; | |
| mScissor.width = width; | |
| mScissor.height = height; | |
| mDirtyBits.set(DIRTY_BIT_SCISSOR); | |
| } | |
| const Rectangle &State::getScissor() const | |
| { | |
| return mScissor; | |
| } | |
| bool State::isDitherEnabled() const | |
| { | |
| return mBlend.dither; | |
| } | |
| void State::setDither(bool enabled) | |
| { | |
| mBlend.dither = enabled; | |
| mDirtyBits.set(DIRTY_BIT_DITHER_ENABLED); | |
| } | |
| bool State::isPrimitiveRestartEnabled() const | |
| { | |
| return mPrimitiveRestart; | |
| } | |
| void State::setPrimitiveRestart(bool enabled) | |
| { | |
| mPrimitiveRestart = enabled; | |
| mDirtyBits.set(DIRTY_BIT_PRIMITIVE_RESTART_ENABLED); | |
| } | |
| void State::setEnableFeature(GLenum feature, bool enabled) | |
| { | |
| switch (feature) | |
| { | |
| case GL_MULTISAMPLE_EXT: setMultisampling(enabled); break; | |
| case GL_SAMPLE_ALPHA_TO_ONE_EXT: setSampleAlphaToOne(enabled); break; | |
| case GL_CULL_FACE: setCullFace(enabled); break; | |
| case GL_POLYGON_OFFSET_FILL: setPolygonOffsetFill(enabled); break; | |
| case GL_SAMPLE_ALPHA_TO_COVERAGE: setSampleAlphaToCoverage(enabled); break; | |
| case GL_SAMPLE_COVERAGE: setSampleCoverage(enabled); break; | |
| case GL_SCISSOR_TEST: setScissorTest(enabled); break; | |
| case GL_STENCIL_TEST: setStencilTest(enabled); break; | |
| case GL_DEPTH_TEST: setDepthTest(enabled); break; | |
| case GL_BLEND: setBlend(enabled); break; | |
| case GL_DITHER: setDither(enabled); break; | |
| case GL_PRIMITIVE_RESTART_FIXED_INDEX: setPrimitiveRestart(enabled); break; | |
| case GL_RASTERIZER_DISCARD: setRasterizerDiscard(enabled); break; | |
| case GL_SAMPLE_MASK: | |
| setSampleMaskEnabled(enabled); | |
| break; | |
| case GL_DEBUG_OUTPUT_SYNCHRONOUS: | |
| mDebug.setOutputSynchronous(enabled); | |
| break; | |
| case GL_DEBUG_OUTPUT: | |
| mDebug.setOutputEnabled(enabled); | |
| break; | |
| case GL_FRAMEBUFFER_SRGB_EXT: | |
| setFramebufferSRGB(enabled); | |
| 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_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; | |
| default: | |
| UNREACHABLE(); | |
| return false; | |
| } | |
| } | |
| void State::setLineWidth(GLfloat width) | |
| { | |
| mLineWidth = width; | |
| mDirtyBits.set(DIRTY_BIT_LINE_WIDTH); | |
| } | |
| float State::getLineWidth() const | |
| { | |
| return mLineWidth; | |
| } | |
| void State::setGenerateMipmapHint(GLenum hint) | |
| { | |
| mGenerateMipmapHint = hint; | |
| mDirtyBits.set(DIRTY_BIT_GENERATE_MIPMAP_HINT); | |
| } | |
| void State::setFragmentShaderDerivativeHint(GLenum hint) | |
| { | |
| mFragmentShaderDerivativeHint = hint; | |
| mDirtyBits.set(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. | |
| } | |
| bool State::isBindGeneratesResourceEnabled() const | |
| { | |
| return mBindGeneratesResource; | |
| } | |
| bool State::areClientArraysEnabled() const | |
| { | |
| return mClientArraysEnabled; | |
| } | |
| void State::setViewportParams(GLint x, GLint y, GLsizei width, GLsizei height) | |
| { | |
| mViewport.x = x; | |
| mViewport.y = y; | |
| mViewport.width = width; | |
| mViewport.height = height; | |
| mDirtyBits.set(DIRTY_BIT_VIEWPORT); | |
| } | |
| const Rectangle &State::getViewport() const | |
| { | |
| return mViewport; | |
| } | |
| void State::setActiveSampler(unsigned int active) | |
| { | |
| mActiveSampler = active; | |
| } | |
| unsigned int State::getActiveSampler() const | |
| { | |
| return static_cast<unsigned int>(mActiveSampler); | |
| } | |
| void State::setSamplerTexture(const Context *context, TextureType type, Texture *texture) | |
| { | |
| mSamplerTextures[type][mActiveSampler].set(context, texture); | |
| mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); | |
| mDirtyObjects.set(DIRTY_OBJECT_PROGRAM_TEXTURES); | |
| } | |
| Texture *State::getTargetTexture(TextureType type) const | |
| { | |
| return getSamplerTexture(static_cast<unsigned int>(mActiveSampler), type); | |
| } | |
| Texture *State::getSamplerTexture(unsigned int sampler, TextureType type) const | |
| { | |
| ASSERT(sampler < mSamplerTextures[type].size()); | |
| return mSamplerTextures[type][sampler].get(); | |
| } | |
| GLuint 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, GLuint 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<TextureType>()) | |
| { | |
| TextureBindingVector &textureVector = mSamplerTextures[type]; | |
| for (BindingPointer<Texture> &binding : textureVector) | |
| { | |
| if (binding.id() == texture) | |
| { | |
| Texture *zeroTexture = zeroTextures[type].get(); | |
| ASSERT(zeroTexture != nullptr); | |
| // Zero textures are the "default" textures instead of NULL | |
| binding.set(context, zeroTexture); | |
| mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); | |
| } | |
| } | |
| } | |
| 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; | |
| break; | |
| } | |
| } | |
| // [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)) | |
| { | |
| mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); | |
| } | |
| } | |
| void State::initializeZeroTextures(const Context *context, const TextureMap &zeroTextures) | |
| { | |
| for (TextureType type : angle::AllEnums<TextureType>()) | |
| { | |
| for (size_t textureUnit = 0; textureUnit < mSamplerTextures[type].size(); ++textureUnit) | |
| { | |
| mSamplerTextures[type][textureUnit].set(context, zeroTextures[type].get()); | |
| } | |
| } | |
| } | |
| void State::setSamplerBinding(const Context *context, GLuint textureUnit, Sampler *sampler) | |
| { | |
| mSamplers[textureUnit].set(context, sampler); | |
| mDirtyBits.set(DIRTY_BIT_SAMPLER_BINDINGS); | |
| mDirtyObjects.set(DIRTY_OBJECT_PROGRAM_TEXTURES); | |
| } | |
| GLuint State::getSamplerId(GLuint textureUnit) const | |
| { | |
| ASSERT(textureUnit < mSamplers.size()); | |
| return mSamplers[textureUnit].id(); | |
| } | |
| Sampler *State::getSampler(GLuint textureUnit) const | |
| { | |
| return mSamplers[textureUnit].get(); | |
| } | |
| void State::detachSampler(const Context *context, GLuint 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 (BindingPointer<Sampler> &samplerBinding : mSamplers) | |
| { | |
| if (samplerBinding.id() == sampler) | |
| { | |
| samplerBinding.set(context, nullptr); | |
| mDirtyBits.set(DIRTY_BIT_SAMPLER_BINDINGS); | |
| } | |
| } | |
| } | |
| void State::setRenderbufferBinding(const Context *context, Renderbuffer *renderbuffer) | |
| { | |
| mRenderbuffer.set(context, renderbuffer); | |
| mDirtyBits.set(DIRTY_BIT_RENDERBUFFER_BINDING); | |
| } | |
| GLuint State::getRenderbufferId() const | |
| { | |
| return mRenderbuffer.id(); | |
| } | |
| Renderbuffer *State::getCurrentRenderbuffer() const | |
| { | |
| return mRenderbuffer.get(); | |
| } | |
| void State::detachRenderbuffer(const Context *context, GLuint 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)) | |
| { | |
| mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); | |
| } | |
| } | |
| } | |
| 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->hasAnyDirtyBit()) | |
| { | |
| mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); | |
| } | |
| } | |
| 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::getReadFramebuffer() const | |
| { | |
| return mReadFramebuffer; | |
| } | |
| Framebuffer *State::getDrawFramebuffer() const | |
| { | |
| return mDrawFramebuffer; | |
| } | |
| bool State::removeReadFramebufferBinding(GLuint framebuffer) | |
| { | |
| if (mReadFramebuffer != nullptr && | |
| mReadFramebuffer->id() == framebuffer) | |
| { | |
| setReadFramebufferBinding(nullptr); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool State::removeDrawFramebufferBinding(GLuint framebuffer) | |
| { | |
| if (mReadFramebuffer != nullptr && | |
| mDrawFramebuffer->id() == framebuffer) | |
| { | |
| setDrawFramebufferBinding(nullptr); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void State::setVertexArrayBinding(VertexArray *vertexArray) | |
| { | |
| mVertexArray = vertexArray; | |
| mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING); | |
| if (mVertexArray && mVertexArray->hasAnyDirtyBit()) | |
| { | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| } | |
| } | |
| GLuint State::getVertexArrayId() const | |
| { | |
| ASSERT(mVertexArray != nullptr); | |
| return mVertexArray->id(); | |
| } | |
| VertexArray *State::getVertexArray() const | |
| { | |
| ASSERT(mVertexArray != nullptr); | |
| return mVertexArray; | |
| } | |
| bool State::removeVertexArrayBinding(GLuint vertexArray) | |
| { | |
| if (mVertexArray->id() == vertexArray) | |
| { | |
| mVertexArray = nullptr; | |
| mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING); | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| return true; | |
| } | |
| return false; | |
| } | |
| 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::setVertexAttribBinding(const Context *context, GLuint attribIndex, GLuint bindingIndex) | |
| { | |
| getVertexArray()->setVertexAttribBinding(context, attribIndex, bindingIndex); | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| } | |
| void State::setVertexAttribFormat(GLuint attribIndex, | |
| GLint size, | |
| GLenum type, | |
| bool normalized, | |
| bool pureInteger, | |
| GLuint relativeOffset) | |
| { | |
| getVertexArray()->setVertexAttribFormat(attribIndex, size, type, normalized, pureInteger, | |
| relativeOffset); | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| } | |
| void State::setVertexBindingDivisor(GLuint bindingIndex, GLuint divisor) | |
| { | |
| getVertexArray()->setVertexBindingDivisor(bindingIndex, divisor); | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| } | |
| void State::setProgram(const Context *context, Program *newProgram) | |
| { | |
| if (mProgram != newProgram) | |
| { | |
| if (mProgram) | |
| { | |
| mProgram->release(context); | |
| } | |
| mProgram = newProgram; | |
| if (mProgram) | |
| { | |
| newProgram->addRef(); | |
| mDirtyObjects.set(DIRTY_OBJECT_PROGRAM_TEXTURES); | |
| } | |
| mDirtyBits.set(DIRTY_BIT_PROGRAM_EXECUTABLE); | |
| mDirtyBits.set(DIRTY_BIT_PROGRAM_BINDING); | |
| } | |
| } | |
| Program *State::getProgram() const | |
| { | |
| return mProgram; | |
| } | |
| void State::setTransformFeedbackBinding(const Context *context, | |
| TransformFeedback *transformFeedback) | |
| { | |
| mTransformFeedback.set(context, transformFeedback); | |
| mDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING); | |
| } | |
| TransformFeedback *State::getCurrentTransformFeedback() const | |
| { | |
| return mTransformFeedback.get(); | |
| } | |
| bool State::isTransformFeedbackActiveUnpaused() const | |
| { | |
| TransformFeedback *curTransformFeedback = getCurrentTransformFeedback(); | |
| return curTransformFeedback && curTransformFeedback->isActive() && !curTransformFeedback->isPaused(); | |
| } | |
| bool State::removeTransformFeedbackBinding(const Context *context, GLuint transformFeedback) | |
| { | |
| if (mTransformFeedback.id() == transformFeedback) | |
| { | |
| mTransformFeedback.set(context, nullptr); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void State::setProgramPipelineBinding(const Context *context, ProgramPipeline *pipeline) | |
| { | |
| mProgramPipeline.set(context, pipeline); | |
| } | |
| void State::detachProgramPipeline(const Context *context, GLuint pipeline) | |
| { | |
| mProgramPipeline.set(context, nullptr); | |
| } | |
| bool State::isQueryActive(const GLenum type) const | |
| { | |
| for (auto &iter : mActiveQueries) | |
| { | |
| const Query *query = iter.second.get(); | |
| if (query != nullptr && ActiveQueryType(query->getType()) == ActiveQueryType(type)) | |
| { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool State::isQueryActive(Query *query) const | |
| { | |
| for (auto &iter : mActiveQueries) | |
| { | |
| if (iter.second.get() == query) | |
| { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void State::setActiveQuery(const Context *context, GLenum target, Query *query) | |
| { | |
| mActiveQueries[target].set(context, query); | |
| } | |
| GLuint State::getActiveQueryId(GLenum target) const | |
| { | |
| const Query *query = getActiveQuery(target); | |
| return (query ? query->id() : 0u); | |
| } | |
| Query *State::getActiveQuery(GLenum target) const | |
| { | |
| const auto it = mActiveQueries.find(target); | |
| // All query types should already exist in the activeQueries map | |
| ASSERT(it != mActiveQueries.end()); | |
| return it->second.get(); | |
| } | |
| void State::setBufferBinding(const Context *context, BufferBinding target, Buffer *buffer) | |
| { | |
| switch (target) | |
| { | |
| case BufferBinding::PixelPack: | |
| mBoundBuffers[target].set(context, buffer); | |
| mDirtyBits.set(DIRTY_BIT_PACK_BUFFER_BINDING); | |
| break; | |
| case BufferBinding::PixelUnpack: | |
| mBoundBuffers[target].set(context, buffer); | |
| mDirtyBits.set(DIRTY_BIT_UNPACK_BUFFER_BINDING); | |
| break; | |
| case BufferBinding::DrawIndirect: | |
| mBoundBuffers[target].set(context, buffer); | |
| mDirtyBits.set(DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING); | |
| break; | |
| case BufferBinding::DispatchIndirect: | |
| mBoundBuffers[target].set(context, buffer); | |
| mDirtyBits.set(DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING); | |
| break; | |
| case BufferBinding::TransformFeedback: | |
| if (mTransformFeedback.get() != nullptr) | |
| { | |
| mTransformFeedback->bindGenericBuffer(context, buffer); | |
| } | |
| break; | |
| case BufferBinding::ElementArray: | |
| getVertexArray()->setElementArrayBuffer(context, buffer); | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| break; | |
| case BufferBinding::ShaderStorage: | |
| mBoundBuffers[target].set(context, buffer); | |
| mDirtyBits.set(DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING); | |
| break; | |
| default: | |
| mBoundBuffers[target].set(context, buffer); | |
| break; | |
| } | |
| } | |
| void State::setIndexedBufferBinding(const Context *context, | |
| BufferBinding target, | |
| GLuint index, | |
| Buffer *buffer, | |
| GLintptr offset, | |
| GLsizeiptr size) | |
| { | |
| setBufferBinding(context, target, buffer); | |
| switch (target) | |
| { | |
| case BufferBinding::TransformFeedback: | |
| mTransformFeedback->bindIndexedBuffer(context, index, buffer, offset, size); | |
| break; | |
| case BufferBinding::Uniform: | |
| mUniformBuffers[index].set(context, buffer, offset, size); | |
| mDirtyBits.set(DIRTY_BIT_UNIFORM_BUFFER_BINDINGS); | |
| break; | |
| case BufferBinding::AtomicCounter: | |
| mAtomicCounterBuffers[index].set(context, buffer, offset, size); | |
| break; | |
| case BufferBinding::ShaderStorage: | |
| mShaderStorageBuffers[index].set(context, buffer, offset, size); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| const OffsetBindingPointer<Buffer> &State::getIndexedUniformBuffer(size_t index) const | |
| { | |
| ASSERT(static_cast<size_t>(index) < mUniformBuffers.size()); | |
| return mUniformBuffers[index]; | |
| } | |
| const OffsetBindingPointer<Buffer> &State::getIndexedAtomicCounterBuffer(size_t index) const | |
| { | |
| ASSERT(static_cast<size_t>(index) < mAtomicCounterBuffers.size()); | |
| return mAtomicCounterBuffers[index]; | |
| } | |
| const OffsetBindingPointer<Buffer> &State::getIndexedShaderStorageBuffer(size_t index) const | |
| { | |
| ASSERT(static_cast<size_t>(index) < mShaderStorageBuffers.size()); | |
| return mShaderStorageBuffers[index]; | |
| } | |
| Buffer *State::getTargetBuffer(BufferBinding target) const | |
| { | |
| switch (target) | |
| { | |
| case BufferBinding::ElementArray: | |
| return getVertexArray()->getElementArrayBuffer().get(); | |
| case BufferBinding::TransformFeedback: | |
| return mTransformFeedback->getGenericBuffer().get(); | |
| default: | |
| return mBoundBuffers[target].get(); | |
| } | |
| } | |
| void State::detachBuffer(const Context *context, GLuint bufferName) | |
| { | |
| for (auto &buffer : mBoundBuffers) | |
| { | |
| if (buffer.id() == bufferName) | |
| { | |
| buffer.set(context, nullptr); | |
| } | |
| } | |
| TransformFeedback *curTransformFeedback = getCurrentTransformFeedback(); | |
| if (curTransformFeedback) | |
| { | |
| curTransformFeedback->detachBuffer(context, bufferName); | |
| } | |
| getVertexArray()->detachBuffer(context, bufferName); | |
| } | |
| 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<size_t>(index) < mVertexAttribCurrentValues.size()); | |
| mVertexAttribCurrentValues[index].setFloatValues(values); | |
| mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES); | |
| mDirtyCurrentValues.set(index); | |
| mCurrentValuesTypeMask.setIndex(GL_FLOAT, index); | |
| } | |
| void State::setVertexAttribu(GLuint index, const GLuint values[4]) | |
| { | |
| ASSERT(static_cast<size_t>(index) < mVertexAttribCurrentValues.size()); | |
| mVertexAttribCurrentValues[index].setUnsignedIntValues(values); | |
| mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES); | |
| mDirtyCurrentValues.set(index); | |
| mCurrentValuesTypeMask.setIndex(GL_UNSIGNED_INT, index); | |
| } | |
| void State::setVertexAttribi(GLuint index, const GLint values[4]) | |
| { | |
| ASSERT(static_cast<size_t>(index) < mVertexAttribCurrentValues.size()); | |
| mVertexAttribCurrentValues[index].setIntValues(values); | |
| mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES); | |
| mDirtyCurrentValues.set(index); | |
| mCurrentValuesTypeMask.setIndex(GL_INT, index); | |
| } | |
| void State::setVertexAttribPointer(const Context *context, | |
| unsigned int attribNum, | |
| Buffer *boundBuffer, | |
| GLint size, | |
| GLenum type, | |
| bool normalized, | |
| bool pureInteger, | |
| GLsizei stride, | |
| const void *pointer) | |
| { | |
| getVertexArray()->setVertexAttribPointer(context, attribNum, boundBuffer, size, type, | |
| normalized, pureInteger, stride, pointer); | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| } | |
| void State::setVertexAttribDivisor(const Context *context, GLuint index, GLuint divisor) | |
| { | |
| getVertexArray()->setVertexAttribDivisor(context, index, divisor); | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| } | |
| const VertexAttribCurrentValueData &State::getVertexAttribCurrentValue(size_t attribNum) const | |
| { | |
| ASSERT(attribNum < mVertexAttribCurrentValues.size()); | |
| return mVertexAttribCurrentValues[attribNum]; | |
| } | |
| const std::vector<VertexAttribCurrentValueData> &State::getVertexAttribCurrentValues() const | |
| { | |
| return mVertexAttribCurrentValues; | |
| } | |
| 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); | |
| } | |
| GLint State::getPackAlignment() const | |
| { | |
| return mPack.alignment; | |
| } | |
| void State::setPackReverseRowOrder(bool reverseRowOrder) | |
| { | |
| mPack.reverseRowOrder = reverseRowOrder; | |
| mDirtyBits.set(DIRTY_BIT_PACK_STATE); | |
| } | |
| bool State::getPackReverseRowOrder() const | |
| { | |
| return mPack.reverseRowOrder; | |
| } | |
| void State::setPackRowLength(GLint rowLength) | |
| { | |
| mPack.rowLength = rowLength; | |
| mDirtyBits.set(DIRTY_BIT_PACK_STATE); | |
| } | |
| GLint State::getPackRowLength() const | |
| { | |
| return mPack.rowLength; | |
| } | |
| void State::setPackSkipRows(GLint skipRows) | |
| { | |
| mPack.skipRows = skipRows; | |
| mDirtyBits.set(DIRTY_BIT_PACK_STATE); | |
| } | |
| GLint State::getPackSkipRows() const | |
| { | |
| return mPack.skipRows; | |
| } | |
| void State::setPackSkipPixels(GLint skipPixels) | |
| { | |
| mPack.skipPixels = skipPixels; | |
| mDirtyBits.set(DIRTY_BIT_PACK_STATE); | |
| } | |
| GLint State::getPackSkipPixels() const | |
| { | |
| return mPack.skipPixels; | |
| } | |
| const PixelPackState &State::getPackState() const | |
| { | |
| return mPack; | |
| } | |
| PixelPackState &State::getPackState() | |
| { | |
| return mPack; | |
| } | |
| void State::setUnpackAlignment(GLint alignment) | |
| { | |
| mUnpack.alignment = alignment; | |
| mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); | |
| } | |
| GLint State::getUnpackAlignment() const | |
| { | |
| return mUnpack.alignment; | |
| } | |
| void State::setUnpackRowLength(GLint rowLength) | |
| { | |
| mUnpack.rowLength = rowLength; | |
| mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); | |
| } | |
| GLint State::getUnpackRowLength() const | |
| { | |
| return mUnpack.rowLength; | |
| } | |
| void State::setUnpackImageHeight(GLint imageHeight) | |
| { | |
| mUnpack.imageHeight = imageHeight; | |
| mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); | |
| } | |
| GLint State::getUnpackImageHeight() const | |
| { | |
| return mUnpack.imageHeight; | |
| } | |
| void State::setUnpackSkipImages(GLint skipImages) | |
| { | |
| mUnpack.skipImages = skipImages; | |
| mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); | |
| } | |
| GLint State::getUnpackSkipImages() const | |
| { | |
| return mUnpack.skipImages; | |
| } | |
| void State::setUnpackSkipRows(GLint skipRows) | |
| { | |
| mUnpack.skipRows = skipRows; | |
| mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); | |
| } | |
| GLint State::getUnpackSkipRows() const | |
| { | |
| return mUnpack.skipRows; | |
| } | |
| void State::setUnpackSkipPixels(GLint skipPixels) | |
| { | |
| mUnpack.skipPixels = skipPixels; | |
| mDirtyBits.set(DIRTY_BIT_UNPACK_STATE); | |
| } | |
| GLint State::getUnpackSkipPixels() const | |
| { | |
| return mUnpack.skipPixels; | |
| } | |
| const PixelUnpackState &State::getUnpackState() const | |
| { | |
| return mUnpack; | |
| } | |
| PixelUnpackState &State::getUnpackState() | |
| { | |
| return mUnpack; | |
| } | |
| const Debug &State::getDebug() const | |
| { | |
| return mDebug; | |
| } | |
| Debug &State::getDebug() | |
| { | |
| return mDebug; | |
| } | |
| void State::setCoverageModulation(GLenum components) | |
| { | |
| mCoverageModulation = components; | |
| mDirtyBits.set(DIRTY_BIT_COVERAGE_MODULATION); | |
| } | |
| GLenum State::getCoverageModulation() const | |
| { | |
| return mCoverageModulation; | |
| } | |
| void State::loadPathRenderingMatrix(GLenum matrixMode, const GLfloat *matrix) | |
| { | |
| if (matrixMode == GL_PATH_MODELVIEW_CHROMIUM) | |
| { | |
| memcpy(mPathMatrixMV, matrix, 16 * sizeof(GLfloat)); | |
| mDirtyBits.set(DIRTY_BIT_PATH_RENDERING_MATRIX_MV); | |
| } | |
| else if (matrixMode == GL_PATH_PROJECTION_CHROMIUM) | |
| { | |
| memcpy(mPathMatrixProj, matrix, 16 * sizeof(GLfloat)); | |
| mDirtyBits.set(DIRTY_BIT_PATH_RENDERING_MATRIX_PROJ); | |
| } | |
| else | |
| { | |
| UNREACHABLE(); | |
| } | |
| } | |
| const GLfloat *State::getPathRenderingMatrix(GLenum which) const | |
| { | |
| if (which == GL_PATH_MODELVIEW_MATRIX_CHROMIUM) | |
| { | |
| return mPathMatrixMV; | |
| } | |
| else if (which == GL_PATH_PROJECTION_MATRIX_CHROMIUM) | |
| { | |
| return mPathMatrixProj; | |
| } | |
| UNREACHABLE(); | |
| return nullptr; | |
| } | |
| void State::setPathStencilFunc(GLenum func, GLint ref, GLuint mask) | |
| { | |
| mPathStencilFunc = func; | |
| mPathStencilRef = ref; | |
| mPathStencilMask = mask; | |
| mDirtyBits.set(DIRTY_BIT_PATH_RENDERING_STENCIL_STATE); | |
| } | |
| GLenum State::getPathStencilFunc() const | |
| { | |
| return mPathStencilFunc; | |
| } | |
| GLint State::getPathStencilRef() const | |
| { | |
| return mPathStencilRef; | |
| } | |
| GLuint State::getPathStencilMask() const | |
| { | |
| return mPathStencilMask; | |
| } | |
| void State::setFramebufferSRGB(bool sRGB) | |
| { | |
| mFramebufferSRGB = sRGB; | |
| mDirtyBits.set(DIRTY_BIT_FRAMEBUFFER_SRGB); | |
| } | |
| bool State::getFramebufferSRGB() const | |
| { | |
| return mFramebufferSRGB; | |
| } | |
| void State::getBooleanv(GLenum pname, GLboolean *params) | |
| { | |
| switch (pname) | |
| { | |
| case GL_SAMPLE_COVERAGE_INVERT: *params = mSampleCoverageInvert; break; | |
| case GL_DEPTH_WRITEMASK: *params = mDepthStencil.depthMask; break; | |
| case GL_COLOR_WRITEMASK: | |
| params[0] = mBlend.colorMaskRed; | |
| params[1] = mBlend.colorMaskGreen; | |
| params[2] = mBlend.colorMaskBlue; | |
| params[3] = mBlend.colorMaskAlpha; | |
| 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 = mBlend.sampleAlphaToCoverage; 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: *params = mBlend.blend; break; | |
| case GL_DITHER: *params = mBlend.dither; 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; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| void State::getFloatv(GLenum pname, GLfloat *params) | |
| { | |
| // 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<GLfloat>(mMultiSampling); | |
| break; | |
| case GL_SAMPLE_ALPHA_TO_ONE_EXT: | |
| *params = static_cast<GLfloat>(mSampleAlphaToOne); | |
| break; | |
| case GL_COVERAGE_MODULATION_CHROMIUM: | |
| params[0] = static_cast<GLfloat>(mCoverageModulation); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| void State::getIntegerv(const Context *context, GLenum pname, GLint *params) | |
| { | |
| if (pname >= GL_DRAW_BUFFER0_EXT && pname <= GL_DRAW_BUFFER15_EXT) | |
| { | |
| unsigned int colorAttachment = (pname - GL_DRAW_BUFFER0_EXT); | |
| ASSERT(colorAttachment < mMaxDrawBuffers); | |
| Framebuffer *framebuffer = mDrawFramebuffer; | |
| *params = framebuffer->getDrawBufferState(colorAttachment); | |
| return; | |
| } | |
| // 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(); | |
| break; | |
| case GL_DRAW_INDIRECT_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::DrawIndirect].id(); | |
| break; | |
| case GL_ELEMENT_ARRAY_BUFFER_BINDING: | |
| *params = getVertexArray()->getElementArrayBuffer().id(); | |
| break; | |
| //case GL_FRAMEBUFFER_BINDING: // now equivalent to GL_DRAW_FRAMEBUFFER_BINDING_ANGLE | |
| case GL_DRAW_FRAMEBUFFER_BINDING_ANGLE: *params = mDrawFramebuffer->id(); break; | |
| case GL_READ_FRAMEBUFFER_BINDING_ANGLE: *params = mReadFramebuffer->id(); break; | |
| case GL_RENDERBUFFER_BINDING: *params = mRenderbuffer.id(); break; | |
| case GL_VERTEX_ARRAY_BINDING: *params = mVertexArray->id(); break; | |
| case GL_CURRENT_PROGRAM: *params = mProgram ? mProgram->id() : 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_FRAGMENT_SHADER_DERIVATIVE_HINT_OES: *params = mFragmentShaderDerivativeHint; break; | |
| case GL_ACTIVE_TEXTURE: | |
| *params = (static_cast<GLint>(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(context, 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(context, 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: *params = mBlend.sourceBlendRGB; break; | |
| case GL_BLEND_SRC_ALPHA: *params = mBlend.sourceBlendAlpha; break; | |
| case GL_BLEND_DST_RGB: *params = mBlend.destBlendRGB; break; | |
| case GL_BLEND_DST_ALPHA: *params = mBlend.destBlendAlpha; break; | |
| case GL_BLEND_EQUATION_RGB: *params = mBlend.blendEquationRGB; break; | |
| case GL_BLEND_EQUATION_ALPHA: *params = mBlend.blendEquationAlpha; break; | |
| case GL_STENCIL_WRITEMASK: | |
| *params = CastMaskValue(context, mDepthStencil.stencilWritemask); | |
| break; | |
| case GL_STENCIL_BACK_WRITEMASK: | |
| *params = CastMaskValue(context, 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->checkStatus(context) == GL_FRAMEBUFFER_COMPLETE) | |
| { | |
| switch (pname) | |
| { | |
| case GL_SAMPLE_BUFFERS: | |
| if (framebuffer->getSamples(context) != 0) | |
| { | |
| *params = 1; | |
| } | |
| else | |
| { | |
| *params = 0; | |
| } | |
| break; | |
| case GL_SAMPLES: | |
| *params = framebuffer->getSamples(context); | |
| 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->getFirstColorbuffer(); | |
| 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->getDepthbuffer(); | |
| if (depthbuffer) | |
| { | |
| *params = depthbuffer->getDepthSize(); | |
| } | |
| else | |
| { | |
| *params = 0; | |
| } | |
| } | |
| break; | |
| case GL_STENCIL_BITS: | |
| { | |
| const Framebuffer *framebuffer = getDrawFramebuffer(); | |
| const FramebufferAttachment *stencilbuffer = framebuffer->getStencilbuffer(); | |
| if (stencilbuffer) | |
| { | |
| *params = stencilbuffer->getStencilSize(); | |
| } | |
| else | |
| { | |
| *params = 0; | |
| } | |
| } | |
| break; | |
| case GL_TEXTURE_BINDING_2D: | |
| ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits); | |
| *params = getSamplerTextureId(static_cast<unsigned int>(mActiveSampler), TextureType::_2D); | |
| break; | |
| case GL_TEXTURE_BINDING_RECTANGLE_ANGLE: | |
| ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits); | |
| *params = getSamplerTextureId(static_cast<unsigned int>(mActiveSampler), | |
| TextureType::Rectangle); | |
| break; | |
| case GL_TEXTURE_BINDING_CUBE_MAP: | |
| ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits); | |
| *params = | |
| getSamplerTextureId(static_cast<unsigned int>(mActiveSampler), TextureType::CubeMap); | |
| break; | |
| case GL_TEXTURE_BINDING_3D: | |
| ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits); | |
| *params = getSamplerTextureId(static_cast<unsigned int>(mActiveSampler), TextureType::_3D); | |
| break; | |
| case GL_TEXTURE_BINDING_2D_ARRAY: | |
| ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits); | |
| *params = | |
| getSamplerTextureId(static_cast<unsigned int>(mActiveSampler), TextureType::_2DArray); | |
| break; | |
| case GL_TEXTURE_BINDING_2D_MULTISAMPLE: | |
| ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits); | |
| *params = getSamplerTextureId(static_cast<unsigned int>(mActiveSampler), | |
| TextureType::_2DMultisample); | |
| break; | |
| case GL_TEXTURE_BINDING_EXTERNAL_OES: | |
| ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits); | |
| *params = | |
| getSamplerTextureId(static_cast<unsigned int>(mActiveSampler), TextureType::External); | |
| break; | |
| case GL_UNIFORM_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::Uniform].id(); | |
| break; | |
| case GL_TRANSFORM_FEEDBACK_BINDING: | |
| *params = mTransformFeedback.id(); | |
| break; | |
| case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: | |
| ASSERT(mTransformFeedback.get() != nullptr); | |
| *params = mTransformFeedback->getGenericBuffer().id(); | |
| break; | |
| case GL_COPY_READ_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::CopyRead].id(); | |
| break; | |
| case GL_COPY_WRITE_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::CopyWrite].id(); | |
| break; | |
| case GL_PIXEL_PACK_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::PixelPack].id(); | |
| break; | |
| case GL_PIXEL_UNPACK_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::PixelUnpack].id(); | |
| break; | |
| case GL_READ_BUFFER: | |
| *params = mReadFramebuffer->getReadBufferState(); | |
| break; | |
| case GL_SAMPLER_BINDING: | |
| ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits); | |
| *params = getSamplerId(static_cast<GLuint>(mActiveSampler)); | |
| break; | |
| case GL_DEBUG_LOGGED_MESSAGES: | |
| *params = static_cast<GLint>(mDebug.getMessageCount()); | |
| break; | |
| case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH: | |
| *params = static_cast<GLint>(mDebug.getNextMessageLength()); | |
| break; | |
| case GL_DEBUG_GROUP_STACK_DEPTH: | |
| *params = static_cast<GLint>(mDebug.getGroupStackDepth()); | |
| break; | |
| case GL_MULTISAMPLE_EXT: | |
| *params = static_cast<GLint>(mMultiSampling); | |
| break; | |
| case GL_SAMPLE_ALPHA_TO_ONE_EXT: | |
| *params = static_cast<GLint>(mSampleAlphaToOne); | |
| break; | |
| case GL_COVERAGE_MODULATION_CHROMIUM: | |
| *params = static_cast<GLint>(mCoverageModulation); | |
| break; | |
| case GL_ATOMIC_COUNTER_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::AtomicCounter].id(); | |
| break; | |
| case GL_SHADER_STORAGE_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::ShaderStorage].id(); | |
| break; | |
| case GL_DISPATCH_INDIRECT_BUFFER_BINDING: | |
| *params = mBoundBuffers[BufferBinding::DispatchIndirect].id(); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| void State::getPointerv(GLenum pname, void **params) const | |
| { | |
| switch (pname) | |
| { | |
| case GL_DEBUG_CALLBACK_FUNCTION: | |
| *params = reinterpret_cast<void *>(mDebug.getCallback()); | |
| break; | |
| case GL_DEBUG_CALLBACK_USER_PARAM: | |
| *params = const_cast<void *>(mDebug.getUserParam()); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| void State::getIntegeri_v(GLenum target, GLuint index, GLint *data) | |
| { | |
| switch (target) | |
| { | |
| case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: | |
| ASSERT(static_cast<size_t>(index) < mTransformFeedback->getIndexedBufferCount()); | |
| *data = mTransformFeedback->getIndexedBuffer(index).id(); | |
| break; | |
| case GL_UNIFORM_BUFFER_BINDING: | |
| ASSERT(static_cast<size_t>(index) < mUniformBuffers.size()); | |
| *data = mUniformBuffers[index].id(); | |
| break; | |
| case GL_ATOMIC_COUNTER_BUFFER_BINDING: | |
| ASSERT(static_cast<size_t>(index) < mAtomicCounterBuffers.size()); | |
| *data = mAtomicCounterBuffers[index].id(); | |
| break; | |
| case GL_SHADER_STORAGE_BUFFER_BINDING: | |
| ASSERT(static_cast<size_t>(index) < mShaderStorageBuffers.size()); | |
| *data = mShaderStorageBuffers[index].id(); | |
| break; | |
| case GL_VERTEX_BINDING_BUFFER: | |
| ASSERT(static_cast<size_t>(index) < mVertexArray->getMaxBindings()); | |
| *data = mVertexArray->getVertexBinding(index).getBuffer().id(); | |
| break; | |
| case GL_VERTEX_BINDING_DIVISOR: | |
| ASSERT(static_cast<size_t>(index) < mVertexArray->getMaxBindings()); | |
| *data = mVertexArray->getVertexBinding(index).getDivisor(); | |
| break; | |
| case GL_VERTEX_BINDING_OFFSET: | |
| ASSERT(static_cast<size_t>(index) < mVertexArray->getMaxBindings()); | |
| *data = static_cast<GLuint>(mVertexArray->getVertexBinding(index).getOffset()); | |
| break; | |
| case GL_VERTEX_BINDING_STRIDE: | |
| ASSERT(static_cast<size_t>(index) < mVertexArray->getMaxBindings()); | |
| *data = mVertexArray->getVertexBinding(index).getStride(); | |
| break; | |
| case GL_SAMPLE_MASK_VALUE: | |
| ASSERT(static_cast<size_t>(index) < mSampleMaskValues.size()); | |
| *data = mSampleMaskValues[index]; | |
| break; | |
| case GL_IMAGE_BINDING_NAME: | |
| ASSERT(static_cast<size_t>(index) < mImageUnits.size()); | |
| *data = mImageUnits[index].texture.id(); | |
| break; | |
| case GL_IMAGE_BINDING_LEVEL: | |
| ASSERT(static_cast<size_t>(index) < mImageUnits.size()); | |
| *data = mImageUnits[index].level; | |
| break; | |
| case GL_IMAGE_BINDING_LAYER: | |
| ASSERT(static_cast<size_t>(index) < mImageUnits.size()); | |
| *data = mImageUnits[index].layer; | |
| break; | |
| case GL_IMAGE_BINDING_ACCESS: | |
| ASSERT(static_cast<size_t>(index) < mImageUnits.size()); | |
| *data = mImageUnits[index].access; | |
| break; | |
| case GL_IMAGE_BINDING_FORMAT: | |
| ASSERT(static_cast<size_t>(index) < mImageUnits.size()); | |
| *data = mImageUnits[index].format; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| void State::getInteger64i_v(GLenum target, GLuint index, GLint64 *data) | |
| { | |
| switch (target) | |
| { | |
| case GL_TRANSFORM_FEEDBACK_BUFFER_START: | |
| ASSERT(static_cast<size_t>(index) < mTransformFeedback->getIndexedBufferCount()); | |
| *data = mTransformFeedback->getIndexedBuffer(index).getOffset(); | |
| break; | |
| case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: | |
| ASSERT(static_cast<size_t>(index) < mTransformFeedback->getIndexedBufferCount()); | |
| *data = mTransformFeedback->getIndexedBuffer(index).getSize(); | |
| break; | |
| case GL_UNIFORM_BUFFER_START: | |
| ASSERT(static_cast<size_t>(index) < mUniformBuffers.size()); | |
| *data = mUniformBuffers[index].getOffset(); | |
| break; | |
| case GL_UNIFORM_BUFFER_SIZE: | |
| ASSERT(static_cast<size_t>(index) < mUniformBuffers.size()); | |
| *data = mUniformBuffers[index].getSize(); | |
| break; | |
| case GL_ATOMIC_COUNTER_BUFFER_START: | |
| ASSERT(static_cast<size_t>(index) < mAtomicCounterBuffers.size()); | |
| *data = mAtomicCounterBuffers[index].getOffset(); | |
| break; | |
| case GL_ATOMIC_COUNTER_BUFFER_SIZE: | |
| ASSERT(static_cast<size_t>(index) < mAtomicCounterBuffers.size()); | |
| *data = mAtomicCounterBuffers[index].getSize(); | |
| break; | |
| case GL_SHADER_STORAGE_BUFFER_START: | |
| ASSERT(static_cast<size_t>(index) < mShaderStorageBuffers.size()); | |
| *data = mShaderStorageBuffers[index].getOffset(); | |
| break; | |
| case GL_SHADER_STORAGE_BUFFER_SIZE: | |
| ASSERT(static_cast<size_t>(index) < mShaderStorageBuffers.size()); | |
| *data = mShaderStorageBuffers[index].getSize(); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| void State::getBooleani_v(GLenum target, GLuint index, GLboolean *data) | |
| { | |
| switch (target) | |
| { | |
| case GL_IMAGE_BINDING_LAYERED: | |
| ASSERT(static_cast<size_t>(index) < mImageUnits.size()); | |
| *data = mImageUnits[index].layered; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| bool State::hasMappedBuffer(BufferBinding target) const | |
| { | |
| if (target == BufferBinding::Array) | |
| { | |
| const VertexArray *vao = getVertexArray(); | |
| const auto &vertexAttribs = vao->getVertexAttributes(); | |
| const auto &vertexBindings = vao->getVertexBindings(); | |
| for (size_t attribIndex : vao->getEnabledAttributesMask()) | |
| { | |
| const VertexAttribute &vertexAttrib = vertexAttribs[attribIndex]; | |
| auto *boundBuffer = vertexBindings[vertexAttrib.bindingIndex].getBuffer().get(); | |
| if (vertexAttrib.enabled && boundBuffer && boundBuffer->isMapped()) | |
| { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| else | |
| { | |
| Buffer *buffer = getTargetBuffer(target); | |
| return (buffer && buffer->isMapped()); | |
| } | |
| } | |
| Error State::syncDirtyObjects(const Context *context) | |
| { | |
| if (!mDirtyObjects.any()) | |
| return NoError(); | |
| return syncDirtyObjects(context, mDirtyObjects); | |
| } | |
| Error State::syncDirtyObjects(const Context *context, const DirtyObjects &bitset) | |
| { | |
| for (auto dirtyObject : bitset) | |
| { | |
| switch (dirtyObject) | |
| { | |
| case DIRTY_OBJECT_READ_FRAMEBUFFER: | |
| ASSERT(mReadFramebuffer); | |
| mReadFramebuffer->syncState(context); | |
| break; | |
| case DIRTY_OBJECT_DRAW_FRAMEBUFFER: | |
| ASSERT(mDrawFramebuffer); | |
| mDrawFramebuffer->syncState(context); | |
| break; | |
| case DIRTY_OBJECT_VERTEX_ARRAY: | |
| ASSERT(mVertexArray); | |
| mVertexArray->syncState(context); | |
| break; | |
| case DIRTY_OBJECT_PROGRAM_TEXTURES: | |
| syncProgramTextures(context); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| mDirtyObjects &= ~bitset; | |
| return NoError(); | |
| } | |
| void State::syncProgramTextures(const Context *context) | |
| { | |
| // TODO(jmadill): Fine-grained updates. | |
| if (!mProgram) | |
| { | |
| return; | |
| } | |
| ASSERT(mDirtyObjects[DIRTY_OBJECT_PROGRAM_TEXTURES]); | |
| mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); | |
| ActiveTextureMask newActiveTextures; | |
| // Initialize to the 'Initialized' state and set to 'MayNeedInit' if any texture is not | |
| // initialized. | |
| mCachedTexturesInitState = InitState::Initialized; | |
| for (const SamplerBinding &samplerBinding : mProgram->getSamplerBindings()) | |
| { | |
| if (samplerBinding.unreferenced) | |
| continue; | |
| TextureType textureType = samplerBinding.textureType; | |
| for (GLuint textureUnitIndex : samplerBinding.boundTextureUnits) | |
| { | |
| Texture *texture = getSamplerTexture(textureUnitIndex, textureType); | |
| Sampler *sampler = getSampler(textureUnitIndex); | |
| ASSERT(static_cast<size_t>(textureUnitIndex) < mCompleteTextureCache.size()); | |
| ASSERT(static_cast<size_t>(textureUnitIndex) < newActiveTextures.size()); | |
| ASSERT(texture); | |
| // Mark the texture binding bit as dirty if the texture completeness changes. | |
| // TODO(jmadill): Use specific dirty bit for completeness change. | |
| if (texture->isSamplerComplete(context, sampler) && | |
| !mDrawFramebuffer->hasTextureAttachment(texture)) | |
| { | |
| texture->syncState(); | |
| mCompleteTextureCache[textureUnitIndex] = texture; | |
| } | |
| else | |
| { | |
| mCompleteTextureCache[textureUnitIndex] = nullptr; | |
| } | |
| // Bind the texture unconditionally, to recieve completeness change notifications. | |
| mCompleteTextureBindings[textureUnitIndex].bind(texture->getSubject()); | |
| mActiveTexturesMask.set(textureUnitIndex); | |
| newActiveTextures.set(textureUnitIndex); | |
| if (sampler != nullptr) | |
| { | |
| sampler->syncState(context); | |
| } | |
| if (texture->initState() == InitState::MayNeedInit) | |
| { | |
| mCachedTexturesInitState = InitState::MayNeedInit; | |
| } | |
| } | |
| } | |
| // Unset now missing textures. | |
| ActiveTextureMask negativeMask = mActiveTexturesMask & ~newActiveTextures; | |
| if (negativeMask.any()) | |
| { | |
| for (auto textureIndex : negativeMask) | |
| { | |
| mCompleteTextureBindings[textureIndex].reset(); | |
| mCompleteTextureCache[textureIndex] = nullptr; | |
| mActiveTexturesMask.reset(textureIndex); | |
| } | |
| } | |
| } | |
| Error 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: | |
| case GL_SAMPLER: | |
| case GL_PROGRAM: | |
| localSet.set(DIRTY_OBJECT_PROGRAM_TEXTURES); | |
| break; | |
| } | |
| return syncDirtyObjects(context, localSet); | |
| } | |
| void State::setObjectDirty(GLenum target) | |
| { | |
| switch (target) | |
| { | |
| case GL_READ_FRAMEBUFFER: | |
| mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); | |
| break; | |
| case GL_DRAW_FRAMEBUFFER: | |
| mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); | |
| break; | |
| case GL_FRAMEBUFFER: | |
| mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); | |
| mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); | |
| break; | |
| case GL_VERTEX_ARRAY: | |
| mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY); | |
| break; | |
| case GL_TEXTURE: | |
| case GL_SAMPLER: | |
| case GL_PROGRAM: | |
| mDirtyObjects.set(DIRTY_OBJECT_PROGRAM_TEXTURES); | |
| mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); | |
| break; | |
| } | |
| } | |
| void State::setFramebufferDirty(const Framebuffer *framebuffer) const | |
| { | |
| if (framebuffer == mReadFramebuffer) | |
| { | |
| mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER); | |
| } | |
| if (framebuffer == mDrawFramebuffer) | |
| { | |
| mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER); | |
| } | |
| } | |
| void State::onProgramExecutableChange(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." | |
| if (program->isLinked() && mProgram == program) | |
| { | |
| mDirtyBits.set(DIRTY_BIT_PROGRAM_EXECUTABLE); | |
| mDirtyObjects.set(DIRTY_OBJECT_PROGRAM_TEXTURES); | |
| } | |
| } | |
| void State::setImageUnit(const Context *context, | |
| GLuint unit, | |
| Texture *texture, | |
| GLint level, | |
| GLboolean layered, | |
| GLint layer, | |
| GLenum access, | |
| GLenum format) | |
| { | |
| mImageUnits[unit].texture.set(context, texture); | |
| mImageUnits[unit].level = level; | |
| mImageUnits[unit].layered = layered; | |
| mImageUnits[unit].layer = layer; | |
| mImageUnits[unit].access = access; | |
| mImageUnits[unit].format = format; | |
| } | |
| const ImageUnit &State::getImageUnit(GLuint unit) const | |
| { | |
| return mImageUnits[unit]; | |
| } | |
| // Handle a dirty texture event. | |
| void State::onSubjectStateChange(const Context *context, | |
| angle::SubjectIndex index, | |
| angle::SubjectMessage message) | |
| { | |
| // Conservatively assume all textures are dirty. | |
| // TODO(jmadill): More fine-grained update. | |
| mDirtyObjects.set(DIRTY_OBJECT_PROGRAM_TEXTURES); | |
| if (!mCompleteTextureCache[index] || | |
| mCompleteTextureCache[index]->initState() == InitState::MayNeedInit) | |
| { | |
| mCachedTexturesInitState = InitState::MayNeedInit; | |
| } | |
| } | |
| Error State::clearUnclearedActiveTextures(const Context *context) | |
| { | |
| ASSERT(mRobustResourceInit); | |
| if (mCachedTexturesInitState == InitState::Initialized) | |
| { | |
| return NoError(); | |
| } | |
| for (auto textureIndex : mActiveTexturesMask) | |
| { | |
| Texture *texture = mCompleteTextureCache[textureIndex]; | |
| if (texture) | |
| { | |
| ANGLE_TRY(texture->ensureInitialized(context)); | |
| } | |
| } | |
| mCachedTexturesInitState = InitState::Initialized; | |
| return NoError(); | |
| } | |
| AttributesMask State::getAndResetDirtyCurrentValues() const | |
| { | |
| AttributesMask retVal = mDirtyCurrentValues; | |
| mDirtyCurrentValues.reset(); | |
| return retVal; | |
| } | |
| } // namespace gl |