// // Copyright 2014 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //
// State.cpp: Implements the State class, encapsulating raw GL state.
// Mapping from a buffer binding type to a dirty bit type.
constexpr angle::PackedEnumMap<BufferBinding, size_t> kBufferBindingDirtyBits = {{
{BufferBinding::AtomicCounter, State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING},
{BufferBinding::DispatchIndirect, State::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING},
{BufferBinding::DrawIndirect, State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING},
{BufferBinding::PixelPack, State::DIRTY_BIT_PACK_BUFFER_BINDING},
{BufferBinding::PixelUnpack, State::DIRTY_BIT_UNPACK_BUFFER_BINDING},
{BufferBinding::ShaderStorage, State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING},
{BufferBinding::Uniform, State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS},
}};
// Returns a buffer binding function depending on if a dirty bit is set. template <BufferBinding Target>
constexpr std::pair<BufferBinding, State::BufferBindingSetter> GetBufferBindingSetter()
{ return std::make_pair(Target, kBufferBindingDirtyBits[Target] != 0
? &State::setGenericBufferBindingWithBit<Target>
: &State::setGenericBufferBinding<Target>);
}
template <typename T> using ContextStateMember = T *(State::*);
template <typename T>
T *AllocateOrGetSharedResourceManager(const State *shareContextState,
ContextStateMember<T> member,
T *shareResources = nullptr)
{ if (shareContextState)
{
T *resourceManager = (*shareContextState).*member;
ASSERT(!resourceManager || resourceManager == shareResources || !shareResources);
resourceManager->addRef(); return resourceManager;
} elseif (shareResources)
{
shareResources->addRef(); return shareResources;
} else
{ returnnew T();
}
}
// TODO(https://anglebug.com/3889): Remove this helper function after blink and chromium part // refactory done. bool IsTextureCompatibleWithSampler(TextureType texture, TextureType sampler)
{ if (sampler == texture)
{ returntrue;
}
if (sampler == TextureType::VideoImage)
{ if (texture == TextureType::VideoImage || texture == TextureType::_2D)
{ returntrue;
}
}
// These template functions must be defined before they are instantiated in kBufferSetters. template <BufferBinding Target> void State::setGenericBufferBindingWithBit(const Context *context, Buffer *buffer)
{ if (context->isWebGL())
{
UpdateNonTFBufferBindingWebGL(context, &mBoundBuffers[Target], buffer);
} else
{
mBoundBuffers[Target].set(context, buffer);
}
mDirtyBits.set(kBufferBindingDirtyBits[Target]);
}
// Set all indexes in state attributes type mask to float (default) for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
SetComponentTypeMask(ComponentType::Float, i, &mCurrentValuesTypeMask);
}
// GLES1 emulation: Initialize state for GLES1 if version applies // TODO(http://anglebug.com/3745): When on desktop client only do this in compatibility profile if (clientVersion < Version(2, 0) || mClientType == EGL_OPENGL_API)
{
mGLES1State.initialize(context, this);
}
}
void State::reset(const Context *context)
{ // Force a sync so clear doesn't end up dereferencing stale pointers.
(void)syncActiveTextures(context, Command::Other);
mActiveTexturesCache.clear();
for (TextureBindingVector &bindingVec : mSamplerTextures)
{ for (BindingPointer<Texture> &texBinding : bindingVec)
{
texBinding.set(context, nullptr);
}
} for (size_t samplerIdx = 0; samplerIdx < mSamplers.size(); samplerIdx++)
{
mSamplers[samplerIdx].set(context, nullptr);
}
if (texture->hasAnyDirtyBit())
{
setTextureDirty(textureIndex);
}
if (mRobustResourceInit && texture->initState() == InitState::MayNeedInit)
{
mDirtyObjects.set(DIRTY_OBJECT_TEXTURES_INIT);
}
// This cache is updated immediately because we use the cache in the validation layer. // If we defer the update until syncState it's too late and we've already passed validation. if (texture && mExecutable)
{ // It is invalid to try to sample a non-yuv texture with a yuv sampler.
mTexturesIncompatibleWithSamplers[textureIndex] =
mExecutable->getActiveYUVSamplers().test(textureIndex) && !texture->isYUV();
bool State::allActiveDrawBufferChannelsMasked() const
{ // Compare current color mask with all-disabled color mask, while ignoring disabled draw // buffers. return (mBlendStateExt.compareColorMask(0) & mDrawFramebuffer->getDrawBufferMask()).none();
}
bool State::anyActiveDrawBufferChannelMasked() const
{ // Compare current color mask with all-enabled color mask, while ignoring disabled draw // buffers. return (mBlendStateExt.compareColorMask(mBlendStateExt.getAllColorMaskBits()) &
mDrawFramebuffer->getDrawBufferMask())
.any();
}
void State::setBlendColor(float red, float green, float blue, float alpha)
{ // In ES2 without render-to-float extensions, BlendColor clamps to [0,1] on store. // On ES3+, or with render-to-float exts enabled, it does not clamp on store. constbool isES2 = mClientVersion.major == 2; constbool hasFloatBlending =
mExtensions.colorBufferFloatEXT || mExtensions.colorBufferHalfFloatEXT ||
mExtensions.colorBufferFloatRgbCHROMIUM || mExtensions.colorBufferFloatRgbaCHROMIUM; if ((isES2 && !hasFloatBlending) || mNoUnclampedBlendColor)
{
red = clamp01(red);
green = clamp01(green);
blue = clamp01(blue);
alpha = clamp01(alpha);
}
if (mBlendColor.red != red || mBlendColor.green != green || mBlendColor.blue != blue ||
mBlendColor.alpha != alpha)
{
mBlendColor.red = red;
mBlendColor.green = green;
mBlendColor.blue = blue;
mBlendColor.alpha = alpha;
mDirtyBits.set(DIRTY_BIT_BLEND_COLOR);
}
}
void State::setSampleMaskParams(GLuint maskNumber, GLbitfield mask)
{
ASSERT(maskNumber < mMaxSampleMaskWords);
mSampleMaskValues[maskNumber] = mask; // TODO(jmadill): Use a child dirty bit if we ever use more than two words.
mDirtyBits.set(DIRTY_BIT_SAMPLE_MASK);
}
void State::setEnableFeature(GLenum feature, bool enabled)
{ switch (feature)
{ case GL_MULTISAMPLE_EXT:
setMultisampling(enabled); return; case GL_SAMPLE_ALPHA_TO_ONE_EXT:
setSampleAlphaToOne(enabled); return; case GL_CULL_FACE:
setCullFace(enabled); return; case GL_POLYGON_OFFSET_FILL:
setPolygonOffsetFill(enabled); return; case GL_SAMPLE_ALPHA_TO_COVERAGE:
setSampleAlphaToCoverage(enabled); return; case GL_SAMPLE_COVERAGE:
setSampleCoverage(enabled); return; case GL_SCISSOR_TEST:
setScissorTest(enabled); return; case GL_STENCIL_TEST:
setStencilTest(enabled); return; case GL_DEPTH_TEST:
setDepthTest(enabled); return; case GL_BLEND:
setBlend(enabled); return; case GL_DITHER:
setDither(enabled); return; case GL_COLOR_LOGIC_OP: if (mClientVersion.major == 1)
{ // Handle logicOp in GLES1 through the GLES1 state management and emulation. // Otherwise this state could be set as part of ANGLE_logic_op. break;
}
setLogicOpEnabled(enabled); return; case GL_PRIMITIVE_RESTART_FIXED_INDEX:
setPrimitiveRestart(enabled); return; case GL_RASTERIZER_DISCARD:
setRasterizerDiscard(enabled); return; case GL_SAMPLE_MASK:
setSampleMaskEnabled(enabled); return; case GL_DEBUG_OUTPUT_SYNCHRONOUS:
mDebug.setOutputSynchronous(enabled); return; case GL_DEBUG_OUTPUT:
mDebug.setOutputEnabled(enabled); return; case GL_FRAMEBUFFER_SRGB_EXT:
setFramebufferSRGB(enabled); return; case GL_TEXTURE_RECTANGLE_ANGLE:
mTextureRectangleEnabled = enabled; return; case GL_SAMPLE_SHADING:
setSampleShading(enabled); return; // GL_APPLE_clip_distance/GL_EXT_clip_cull_distance case GL_CLIP_DISTANCE0_EXT: case GL_CLIP_DISTANCE1_EXT: case GL_CLIP_DISTANCE2_EXT: case GL_CLIP_DISTANCE3_EXT: case GL_CLIP_DISTANCE4_EXT: case GL_CLIP_DISTANCE5_EXT: case GL_CLIP_DISTANCE6_EXT: case GL_CLIP_DISTANCE7_EXT: // NOTE(hqle): These enums are conflicted with GLES1's enums, need // to do additional check here: if (mClientVersion.major >= 2)
{
setClipDistanceEnable(feature - GL_CLIP_DISTANCE0_EXT, enabled); return;
} break; case GL_SHADING_RATE_PRESERVE_ASPECT_RATIO_QCOM:
mShadingRatePreserveAspectRatio = enabled; return;
}
ASSERT(mClientVersion.major == 1);
// GLES1 emulation. Need to separate from main switch due to conflict enum between // GL_CLIP_DISTANCE0_EXT & GL_CLIP_PLANE0 switch (feature)
{ case GL_ALPHA_TEST:
mGLES1State.mAlphaTestEnabled = enabled; break; case GL_TEXTURE_2D:
mGLES1State.mTexUnitEnables[mActiveSampler].set(TextureType::_2D, enabled); break; case GL_TEXTURE_CUBE_MAP:
mGLES1State.mTexUnitEnables[mActiveSampler].set(TextureType::CubeMap, enabled); break; case GL_LIGHTING:
mGLES1State.mLightingEnabled = enabled; break; case GL_LIGHT0: case GL_LIGHT1: case GL_LIGHT2: case GL_LIGHT3: case GL_LIGHT4: case GL_LIGHT5: case GL_LIGHT6: case GL_LIGHT7:
mGLES1State.mLights[feature - GL_LIGHT0].enabled = enabled; break; case GL_NORMALIZE:
mGLES1State.mNormalizeEnabled = enabled; break; case GL_RESCALE_NORMAL:
mGLES1State.mRescaleNormalEnabled = enabled; break; case GL_COLOR_MATERIAL:
mGLES1State.mColorMaterialEnabled = enabled; break; case GL_CLIP_PLANE0: case GL_CLIP_PLANE1: case GL_CLIP_PLANE2: case GL_CLIP_PLANE3: case GL_CLIP_PLANE4: case GL_CLIP_PLANE5:
mGLES1State.mClipPlanes[feature - GL_CLIP_PLANE0].enabled = enabled; break; case GL_FOG:
mGLES1State.mFogEnabled = enabled; break; case GL_POINT_SMOOTH:
mGLES1State.mPointSmoothEnabled = enabled; break; case GL_LINE_SMOOTH:
mGLES1State.mLineSmoothEnabled = enabled; break; case GL_POINT_SPRITE_OES:
mGLES1State.mPointSpriteEnabled = enabled; break; case GL_COLOR_LOGIC_OP:
mGLES1State.setLogicOpEnabled(enabled); break; default:
UNREACHABLE();
}
}
bool State::getEnableFeature(GLenum feature) const
{ switch (feature)
{ case GL_MULTISAMPLE_EXT: return isMultisamplingEnabled(); case GL_SAMPLE_ALPHA_TO_ONE_EXT: return isSampleAlphaToOneEnabled(); case GL_CULL_FACE: return isCullFaceEnabled(); case GL_POLYGON_OFFSET_FILL: return isPolygonOffsetFillEnabled(); case GL_SAMPLE_ALPHA_TO_COVERAGE: return isSampleAlphaToCoverageEnabled(); case GL_SAMPLE_COVERAGE: return isSampleCoverageEnabled(); case GL_SCISSOR_TEST: return isScissorTestEnabled(); case GL_STENCIL_TEST: return isStencilTestEnabled(); case GL_DEPTH_TEST: return isDepthTestEnabled(); case GL_BLEND: return isBlendEnabled(); case GL_DITHER: return isDitherEnabled(); case GL_COLOR_LOGIC_OP: if (mClientVersion.major == 1)
{ // Handle logicOp in GLES1 through the GLES1 state management and emulation. break;
} return isLogicOpEnabled(); case GL_PRIMITIVE_RESTART_FIXED_INDEX: return isPrimitiveRestartEnabled(); case GL_RASTERIZER_DISCARD: return isRasterizerDiscardEnabled(); case GL_SAMPLE_MASK: return isSampleMaskEnabled(); case GL_DEBUG_OUTPUT_SYNCHRONOUS: return mDebug.isOutputSynchronous(); case GL_DEBUG_OUTPUT: return mDebug.isOutputEnabled(); case GL_BIND_GENERATES_RESOURCE_CHROMIUM: return isBindGeneratesResourceEnabled(); case GL_CLIENT_ARRAYS_ANGLE: return areClientArraysEnabled(); case GL_FRAMEBUFFER_SRGB_EXT: return getFramebufferSRGB(); case GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE: return mRobustResourceInit; case GL_PROGRAM_CACHE_ENABLED_ANGLE: return mProgramBinaryCacheEnabled; case GL_TEXTURE_RECTANGLE_ANGLE: return mTextureRectangleEnabled; case GL_SAMPLE_SHADING: return isSampleShadingEnabled(); // GL_APPLE_clip_distance/GL_EXT_clip_cull_distance case GL_CLIP_DISTANCE0_EXT: case GL_CLIP_DISTANCE1_EXT: case GL_CLIP_DISTANCE2_EXT: case GL_CLIP_DISTANCE3_EXT: case GL_CLIP_DISTANCE4_EXT: case GL_CLIP_DISTANCE5_EXT: case GL_CLIP_DISTANCE6_EXT: case GL_CLIP_DISTANCE7_EXT: if (mClientVersion.major >= 2)
{ // If GLES version is 1, the GL_CLIP_DISTANCE0_EXT enum will be used as // GL_CLIP_PLANE0 instead. return mClipDistancesEnabled.test(feature - GL_CLIP_DISTANCE0_EXT);
} break; case GL_SHADING_RATE_PRESERVE_ASPECT_RATIO_QCOM: return mShadingRatePreserveAspectRatio;
}
ASSERT(mClientVersion.major == 1);
switch (feature)
{ // GLES1 emulation case GL_ALPHA_TEST: return mGLES1State.mAlphaTestEnabled; case GL_VERTEX_ARRAY: return mGLES1State.mVertexArrayEnabled; case GL_NORMAL_ARRAY: return mGLES1State.mNormalArrayEnabled; case GL_COLOR_ARRAY: return mGLES1State.mColorArrayEnabled; case GL_POINT_SIZE_ARRAY_OES: return mGLES1State.mPointSizeArrayEnabled; case GL_TEXTURE_COORD_ARRAY: return mGLES1State.mTexCoordArrayEnabled[mGLES1State.mClientActiveTexture]; case GL_TEXTURE_2D: return mGLES1State.isTextureTargetEnabled(getActiveSampler(), TextureType::_2D); case GL_TEXTURE_CUBE_MAP: return mGLES1State.isTextureTargetEnabled(getActiveSampler(), TextureType::CubeMap); case GL_LIGHTING: return mGLES1State.mLightingEnabled; case GL_LIGHT0: case GL_LIGHT1: case GL_LIGHT2: case GL_LIGHT3: case GL_LIGHT4: case GL_LIGHT5: case GL_LIGHT6: case GL_LIGHT7: return mGLES1State.mLights[feature - GL_LIGHT0].enabled; case GL_NORMALIZE: return mGLES1State.mNormalizeEnabled; case GL_RESCALE_NORMAL: return mGLES1State.mRescaleNormalEnabled; case GL_COLOR_MATERIAL: return mGLES1State.mColorMaterialEnabled; case GL_CLIP_PLANE0: case GL_CLIP_PLANE1: case GL_CLIP_PLANE2: case GL_CLIP_PLANE3: case GL_CLIP_PLANE4: case GL_CLIP_PLANE5: return mGLES1State.mClipPlanes[feature - GL_CLIP_PLANE0].enabled; case GL_FOG: return mGLES1State.mFogEnabled; case GL_POINT_SMOOTH: return mGLES1State.mPointSmoothEnabled; case GL_LINE_SMOOTH: return mGLES1State.mLineSmoothEnabled; case GL_POINT_SPRITE_OES: return mGLES1State.mPointSpriteEnabled; case GL_COLOR_LOGIC_OP: return mGLES1State.mLogicOpEnabled; default:
UNREACHABLE(); returnfalse;
}
}
void State::setTextureFilteringHint(GLenum hint)
{
mTextureFilteringHint = hint; // Note: we don't add a dirty bit for this flag as it's not expected to be toggled at // runtime.
}
void State::setFragmentShaderDerivativeHint(GLenum hint)
{
mFragmentShaderDerivativeHint = hint;
mDirtyBits.set(DIRTY_BIT_EXTENDED);
mExtendedDirtyBits.set(EXTENDED_DIRTY_BIT_SHADER_DERIVATIVE_HINT); // TODO: Propagate the hint to shader translator so we can write // ddx, ddx_coarse, or ddx_fine depending on the hint. // Ignore for now. It is valid for implementations to ignore hint.
}
void State::setViewportParams(GLint x, GLint y, GLsizei width, GLsizei height)
{ // [OpenGL ES 2.0.25] section 2.12.1 page 45: // Viewport width and height are clamped to implementation-dependent maximums when specified.
width = std::min(width, mCaps.maxViewportWidth);
height = std::min(height, mCaps.maxViewportHeight);
// Skip if same viewport info if (mViewport.x != x || mViewport.y != y || mViewport.width != width ||
mViewport.height != height)
{
mViewport.x = x;
mViewport.y = y;
mViewport.width = width;
mViewport.height = height;
mDirtyBits.set(DIRTY_BIT_VIEWPORT);
}
}
void State::detachTexture(const Context *context, const TextureMap &zeroTextures, TextureID texture)
{ // Textures have a detach method on State rather than a simple // removeBinding, because the zero/null texture objects are managed // separately, and don't have to go through the Context's maps or // the ResourceManager.
// [OpenGL ES 2.0.24] section 3.8 page 84: // If a texture object is deleted, it is as if all texture units which are bound to that texture // object are rebound to texture object zero
for (TextureType type : angle::AllEnums<TextureType>())
{
TextureBindingVector &textureVector = mSamplerTextures[type];
for (size_t bindingIndex = 0; bindingIndex < textureVector.size(); ++bindingIndex)
{
BindingPointer<Texture> &binding = textureVector[bindingIndex]; if (binding.id() == texture)
{ // Zero textures are the "default" textures instead of NULL
Texture *zeroTexture = zeroTextures[type].get();
ASSERT(zeroTexture != nullptr); if (mCompleteTextureBindings[bindingIndex].getSubject() == binding.get())
{
updateTextureBinding(context, bindingIndex, zeroTexture);
}
binding.set(context, zeroTexture);
}
}
}
// [OpenGL ES 2.0.24] section 4.4 page 112: // If a texture object is deleted while its image is attached to the currently bound // framebuffer, then it is as if Texture2DAttachment had been called, with a texture of 0, for // each attachment point to which this image was attached in the currently bound framebuffer.
if (mReadFramebuffer && mReadFramebuffer->detachTexture(context, texture))
{
mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER);
}
if (mDrawFramebuffer && mDrawFramebuffer->detachTexture(context, texture))
{
setDrawFramebufferDirty();
}
}
void State::initializeZeroTextures(const Context *context, const TextureMap &zeroTextures)
{ for (TextureType type : angle::AllEnums<TextureType>())
{ for (size_t textureUnit = 0; textureUnit < mSamplerTextures[type].size(); ++textureUnit)
{
mSamplerTextures[type][textureUnit].set(context, zeroTextures[type].get());
}
}
}
mSamplers[textureUnit].set(context, sampler);
mDirtyBits.set(DIRTY_BIT_SAMPLER_BINDINGS); // This is overly conservative as it assumes the sampler has never been bound.
setSamplerDirty(textureUnit);
onActiveTextureChange(context, textureUnit);
}
void State::detachSampler(const Context *context, SamplerID sampler)
{ // [OpenGL ES 3.0.2] section 3.8.2 pages 123-124: // If a sampler object that is currently bound to one or more texture units is // deleted, it is as though BindSampler is called once for each texture unit to // which the sampler is bound, with unit set to the texture unit and sampler set to zero. for (size_t i = 0; i < mSamplers.size(); i++)
{ if (mSamplers[i].id() == sampler)
{
setSamplerBinding(context, static_cast<GLuint>(i), nullptr);
}
}
}
void State::detachRenderbuffer(const Context *context, RenderbufferID renderbuffer)
{ // [OpenGL ES 2.0.24] section 4.4 page 109: // If a renderbuffer that is currently bound to RENDERBUFFER is deleted, it is as though // BindRenderbuffer had been executed with the target RENDERBUFFER and name of zero.
if (mRenderbuffer.id() == renderbuffer)
{
setRenderbufferBinding(context, nullptr);
}
// [OpenGL ES 2.0.24] section 4.4 page 111: // If a renderbuffer object is deleted while its image is attached to the currently bound // framebuffer, then it is as if FramebufferRenderbuffer had been called, with a renderbuffer of // 0, for each attachment point to which this image was attached in the currently bound // framebuffer.
angle::Result State::setProgram(const Context *context, Program *newProgram)
{ if (newProgram && !newProgram->isLinked())
{ // Protect against applications that disable validation and try to use a program that was // not successfully linked.
WARN() << "Attempted to use a program that was not successfully linked"; return angle::Result::Continue;
}
if (mProgram != newProgram)
{ if (mProgram)
{
unsetActiveTextures(mExecutable->getActiveSamplersMask());
mProgram->release(context);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.