blob: ba1bc56b5a5d7a5c72e8a88a93cca336188515f2 [file]
//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// StateManagerGL.h: Defines a class for caching applied OpenGL state
#include "libANGLE/renderer/gl/StateManagerGL.h"
#include "libANGLE/renderer/gl/ContextGL.h"
#include <string.h>
#include <algorithm>
#include <limits>
#include "anglebase/numerics/safe_conversions.h"
#include "common/bitset_utils.h"
#include "common/mathutil.h"
#include "common/matrix_utils.h"
#include "common/unsafe_buffers.h"
#include "libANGLE/Context.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/Query.h"
#include "libANGLE/TransformFeedback.h"
#include "libANGLE/VertexArray.h"
#include "libANGLE/histogram_macros.h"
#include "libANGLE/queryconversions.h"
#include "libANGLE/renderer/gl/BufferGL.h"
#include "libANGLE/renderer/gl/FramebufferGL.h"
#include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/ProgramGL.h"
#include "libANGLE/renderer/gl/QueryGL.h"
#include "libANGLE/renderer/gl/SamplerGL.h"
#include "libANGLE/renderer/gl/TextureGL.h"
#include "libANGLE/renderer/gl/TransformFeedbackGL.h"
#include "libANGLE/renderer/gl/VertexArrayGL.h"
#include "platform/PlatformMethods.h"
namespace rx
{
namespace
{
inline void SetGLBoolState(const FunctionsGL *functions, GLenum name, bool value)
{
if (value)
{
functions->enable(name);
}
else
{
functions->disable(name);
}
}
inline void SetGLIndexedBoolState(const FunctionsGL *functions,
GLenum name,
GLuint index,
bool value)
{
if (value)
{
functions->enablei(name, index);
}
else
{
functions->disablei(name, index);
}
}
inline void SetVertexAttribArrayEnabled(const FunctionsGL *functions, GLuint index, bool value)
{
if (value)
{
functions->enableVertexAttribArray(index);
}
else
{
functions->disableVertexAttribArray(index);
}
}
#if defined(ANGLE_ENABLE_ASSERTS)
# define ANGLE_GL_CHECK_GET_HELPER(functions, getter, name, value) \
do \
{ \
ANGLE_GL_CLEAR_ERRORS(functions); \
functions->getter(name, value); \
GLenum error = functions->getError(); \
(error == GL_NO_ERROR) ? static_cast<void>(0) \
: ERR() << "Querying " << gl::FmtHex(name) << " using " \
<< #getter << " generated error " << gl::FmtHex(error); \
} while (0)
# define ANGLE_GL_CHECK_GET_INDEXED_HELPER(functions, getter, name, index, value) \
do \
{ \
ANGLE_GL_CLEAR_ERRORS(functions); \
functions->getter(name, index, value); \
GLenum error = functions->getError(); \
(error == GL_NO_ERROR) ? static_cast<void>(0) \
: ERR() << "Querying " << gl::FmtHex(name) << " at index " \
<< index << " using " << #getter << " generated error " \
<< gl::FmtHex(error); \
} while (0)
# define ANGLE_GL_CHECK_GET_ENABLED_HELPER(functions, getter, name, value) \
do \
{ \
ANGLE_GL_CLEAR_ERRORS(functions); \
*value = functions->getter(name); \
GLenum error = functions->getError(); \
(error == GL_NO_ERROR) ? static_cast<void>(0) \
: ERR() << "Querying " << gl::FmtHex(name) << " using " \
<< #getter << " generated error " << gl::FmtHex(error); \
} while (0)
# define ANGLE_GL_CHECK_GET_INDEXED_ENABLED_HELPER(functions, getter, name, index, value) \
do \
{ \
ANGLE_GL_CLEAR_ERRORS(functions); \
*value = functions->getter(name, index); \
GLenum error = functions->getError(); \
(error == GL_NO_ERROR) ? static_cast<void>(0) \
: ERR() << "Querying " << gl::FmtHex(name) << " at index " \
<< index << " using " << #getter << " generated error " \
<< gl::FmtHex(error); \
} while (0)
# define ANGLE_GL_CHECK_GET_VERTEX_HELPER(functions, getter, index, name, value) \
do \
{ \
ANGLE_GL_CLEAR_ERRORS(functions); \
functions->getter(index, name, value); \
GLenum error = functions->getError(); \
(error == GL_NO_ERROR) ? static_cast<void>(0) \
: ERR() << "Querying " << gl::FmtHex(name) << " for attribute " \
<< index << " using " << #getter << " generated error " \
<< gl::FmtHex(error); \
} while (0)
#else
# define ANGLE_GL_CHECK_GET_HELPER(functions, getter, name, value) functions->getter(name, value)
# define ANGLE_GL_CHECK_GET_INDEXED_HELPER(functions, getter, name, index, value) \
functions->getter(name, index, value)
# define ANGLE_GL_CHECK_GET_ENABLED_HELPER(functions, getter, name, value) \
*value = functions->getter(name)
# define ANGLE_GL_CHECK_GET_INDEXED_ENABLED_HELPER(functions, getter, name, index, value) \
*value = functions->getter(name, index)
# define ANGLE_GL_CHECK_GET_VERTEX_HELPER(functions, getter, index, name, value) \
functions->getter(index, name, value)
#endif
// Non-indexed GLboolean -> glIsEnabled
void GetEnabledHelper(const FunctionsGL *functions, GLenum name, GLboolean *value)
{
ANGLE_GL_CHECK_GET_ENABLED_HELPER(functions, isEnabled, name, value);
}
// Indexed GLboolean -> glIsEnabledi
void GetEnabledHelper(const FunctionsGL *functions, GLenum name, GLuint index, GLboolean *value)
{
ANGLE_GL_CHECK_GET_INDEXED_ENABLED_HELPER(functions, isEnabledi, name, index, value);
}
// Non-indexed bool -> Non-indexed GLboolean (for enabled checks)
void GetEnabledHelper(const FunctionsGL *functions, GLenum name, bool *value)
{
GLboolean v = gl::ConvertToGLBoolean(*value);
GetEnabledHelper(functions, name, &v);
*value = gl::ConvertToBool(v);
}
// Indexed bool -> Indexed GLboolean (for enabled checks)
void GetEnabledHelper(const FunctionsGL *functions, GLenum name, GLuint index, bool *value)
{
GLboolean v = gl::ConvertToGLBoolean(*value);
GetEnabledHelper(functions, name, index, &v);
*value = gl::ConvertToBool(v);
}
// Non-indexed GLint -> glGetIntegerv
void GetHelper(const FunctionsGL *functions, GLenum name, GLint *value)
{
ANGLE_GL_CHECK_GET_HELPER(functions, getIntegerv, name, value);
}
// Indexed GLint -> glGetIntegeri_v
void GetHelper(const FunctionsGL *functions, GLenum name, GLuint index, GLint *value)
{
ANGLE_GL_CHECK_GET_INDEXED_HELPER(functions, getIntegeri_v, name, index, value);
}
// Non-indexed GLenum -> Non-indexed GLint
void GetHelper(const FunctionsGL *functions, GLenum name, GLenum *value)
{
GLint v = *value;
GetHelper(functions, name, &v);
*value = static_cast<GLenum>(v);
}
// Indexed GLenum -> Indexed GLint
void GetHelper(const FunctionsGL *functions, GLenum name, GLuint index, GLenum *value)
{
GLint v = *value;
GetHelper(functions, name, index, &v);
*value = static_cast<GLenum>(v);
}
// Non-indexed bool -> Non-indexed GLint
void GetHelper(const FunctionsGL *functions, GLenum name, bool *value)
{
GLint v = gl::ConvertToGLBoolean(*value);
GetHelper(functions, name, &v);
*value = gl::ConvertToBool(v);
}
// Indexed GLboolean -> Indexed GLint
void GetHelper(const FunctionsGL *functions, GLenum name, GLuint index, GLboolean *value)
{
GLint v = gl::ConvertToGLBoolean(*value);
GetHelper(functions, name, index, &v);
*value = gl::ConvertToBool(v);
}
// Non-indexed std::array<bool, N> -> Non-indexed GLboolean
template <size_t N>
void GetHelper(const FunctionsGL *functions, GLenum name, std::array<bool, N> *values)
{
std::array<GLint, N> v;
for (size_t i = 0; i < N; i++)
{
v[i] = gl::ConvertToGLBoolean(values->at(i));
}
GetHelper(functions, name, v.data());
for (size_t i = 0; i < N; i++)
{
(*values)[i] = gl::ConvertToBool(v[i]);
}
}
// Indexed std::array<bool, N> -> Indexed GLboolean
template <size_t N>
void GetHelper(const FunctionsGL *functions, GLenum name, GLuint index, std::array<bool, N> *values)
{
std::array<GLint, N> v;
for (size_t i = 0; i < N; i++)
{
v[i] = gl::ConvertToGLBoolean(values->at(i));
}
GetHelper(functions, name, index, v.data());
for (size_t i = 0; i < N; i++)
{
(*values)[i] = gl::ConvertToBool(v[i]);
}
}
// Non-indexed gl::Rectangle -> Non-indexed GLint
void GetHelper(const FunctionsGL *functions, GLenum name, gl::Rectangle *rect)
{
std::array<GLint, 4> v = {rect->x, rect->y, rect->width, rect->height};
GetHelper(functions, name, v.data());
*rect = gl::Rectangle(v[0], v[1], v[2], v[3]);
}
// Non-indexed GLfloat -> glGetFloatv
void GetHelper(const FunctionsGL *functions, GLenum name, GLfloat *value)
{
ANGLE_GL_CHECK_GET_HELPER(functions, getFloatv, name, value);
}
// Non-indexed gl::ColorF -> Non-indexed GLfloat
void GetHelper(const FunctionsGL *functions, GLenum name, gl::ColorF *color)
{
std::array<GLfloat, 4> v = {color->red, color->green, color->blue, color->alpha};
GetHelper(functions, name, v.data());
*color = gl::ColorF(v[0], v[1], v[2], v[3]);
}
// Non-indexed packed enum -> Non-indexed GLint
template <typename InternalEnumType, InternalEnumType MaxSize = InternalEnumType::EnumCount>
void GetHelper(const FunctionsGL *functions, GLenum name, InternalEnumType *internalEnum)
{
GLint v = gl::ToGLenum(*internalEnum);
GetHelper(functions, name, &v);
*internalEnum = gl::FromGLenum<InternalEnumType>(v);
}
// Indexed packed enum -> Indexed GLint
template <typename InternalEnumType, InternalEnumType MaxSize = InternalEnumType::EnumCount>
void GetHelper(const FunctionsGL *functions,
GLenum name,
GLuint index,
InternalEnumType *internalEnum)
{
GLint v = gl::ToGLenum(*internalEnum);
GetHelper(functions, name, index, &v);
*internalEnum = gl::FromGLenum<InternalEnumType>(v);
}
// Non-indexed std::array<packed enum> -> Non-indexed GLint
template <typename InternalEnumType,
size_t N,
InternalEnumType MaxSize = InternalEnumType::EnumCount>
void GetHelper(const FunctionsGL *functions,
GLenum name,
std::array<InternalEnumType, N> *internalEnum)
{
std::array<GLint, N> v;
for (size_t i = 0; i < N; i++)
{
v[i] = gl::ToGLenum(internalEnum->at(i));
}
GetHelper(functions, name, v.data());
for (size_t i = 0; i < N; i++)
{
internalEnum->at(i) = gl::FromGLenum<InternalEnumType>(v[i]);
}
}
// Indexed GLint64 -> glGetInteger64i_v
void GetHelper(const FunctionsGL *functions, GLenum name, GLuint index, GLint64 *value)
{
ANGLE_GL_CHECK_GET_INDEXED_HELPER(functions, getInteger64i_v, name, index, value);
}
// Indexed GLintptr -> Indexed GLint64. Enabled only if GLintptr is not the same as GLint64.
template <typename T = void>
void GetHelper(const FunctionsGL *functions, GLenum name, GLuint index, GLintptr *value)
requires(!std::is_same_v<GLintptr, GLint64>)
{
GLint64 v = *value;
GetHelper(functions, name, index, &v);
*value = static_cast<GLintptr>(v);
}
// GLint -> glGetVertexAttribiv
void GetVertexHelper(const FunctionsGL *functions, GLuint index, GLenum name, GLint *value)
{
ANGLE_GL_CHECK_GET_VERTEX_HELPER(functions, getVertexAttribiv, index, name, value);
}
// GLenum -> GLint
void GetVertexHelper(const FunctionsGL *functions, GLuint index, GLenum name, GLenum *value)
{
GLint v = static_cast<GLint>(*value);
GetVertexHelper(functions, index, name, &v);
*value = static_cast<GLint>(v);
}
// bool -> GLint
void GetVertexHelper(const FunctionsGL *functions, GLuint index, GLenum name, bool *value)
{
GLint v = gl::ConvertToGLBoolean(*value);
GetVertexHelper(functions, index, name, &v);
*value = gl::ConvertToBool(v);
}
// packed enum -> GLint
template <typename InternalEnumType, InternalEnumType MaxSize = InternalEnumType::EnumCount>
void GetVertexHelper(const FunctionsGL *functions,
GLuint index,
GLenum name,
InternalEnumType *internalEnum)
{
GLint v = gl::ToGLenum(*internalEnum);
GetVertexHelper(functions, index, name, &v);
*internalEnum = gl::FromGLenum<InternalEnumType>(v);
}
// const void* -> glGetVertexAttribPointerv
void GetVertexHelper(const FunctionsGL *functions, GLuint index, GLenum name, void **value)
{
ANGLE_GL_CHECK_GET_VERTEX_HELPER(functions, getVertexAttribPointerv, index, name, value);
}
struct ScopedBindDrawFramebuffer
{
ScopedBindDrawFramebuffer(const FunctionsGL *functions, GLuint prevFbo, GLuint fbo)
: functions(functions),
binding(nativegl::SupportsSeparateFramebufferBindings(functions) ? GL_DRAW_FRAMEBUFFER
: GL_FRAMEBUFFER),
prevFbo(prevFbo)
{
functions->bindFramebuffer(binding, fbo);
}
~ScopedBindDrawFramebuffer() { functions->bindFramebuffer(binding, prevFbo); }
const FunctionsGL *functions = nullptr;
GLenum binding = 0;
GLenum prevFbo = 0;
};
struct ScopedBindVAO
{
ScopedBindVAO(const FunctionsGL *functions, GLuint prevVAO, GLuint newVAO)
: functions(functions), prevVAO(prevVAO), newVAO(newVAO)
{
if (newVAO != prevVAO)
{
functions->bindVertexArray(newVAO);
}
}
~ScopedBindVAO()
{
if (newVAO != prevVAO)
{
functions->bindVertexArray(prevVAO);
}
}
const FunctionsGL *functions = nullptr;
GLuint prevVAO = 0;
GLuint newVAO = 0;
};
void QueryContextStateGL(const FunctionsGL *functions,
GLuint framebufferWithStencilBits,
ContextStateGL *state)
{
GetHelper(functions, GL_CURRENT_PROGRAM, &state->program);
if (nativegl::SupportsVertexArrayObjects(functions))
{
GetHelper(functions, GL_VERTEX_ARRAY_BINDING, &state->vao);
}
{
// Query the default VAO state, temporarily bind VAO 0
ScopedBindVAO scopedVAO(functions, state->vao, 0);
QueryVertexArrayStateGL(functions, &state->defaultVAOState);
}
GLint maxVertexAttribs = 0;
GetHelper(functions, GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
maxVertexAttribs = std::min(maxVertexAttribs, static_cast<GLint>(gl::MAX_VERTEX_ATTRIBS));
for (GLint i = 0; i < maxVertexAttribs; i++)
{
// It is only valid to query the vertex attrib data using the same type used to set the data
// but there is no query for the data type. Do our best and query using the type that
// already exists in state.
switch (state->vertexAttribCurrentValues[i].Type)
{
case gl::VertexAttribType::Float:
functions->getVertexAttribfv(
i, GL_CURRENT_VERTEX_ATTRIB,
state->vertexAttribCurrentValues[i].Values.FloatValues);
break;
case gl::VertexAttribType::Int:
functions->getVertexAttribIiv(i, GL_CURRENT_VERTEX_ATTRIB,
state->vertexAttribCurrentValues[i].Values.IntValues);
break;
case gl::VertexAttribType::UnsignedInt:
functions->getVertexAttribIuiv(
i, GL_CURRENT_VERTEX_ATTRIB,
state->vertexAttribCurrentValues[i].Values.UnsignedIntValues);
break;
default:
UNREACHABLE();
}
}
for (gl::BufferBinding bufferBinding : angle::AllEnums<gl::BufferBinding>())
{
if (!nativegl::SupportsBufferBinding(functions, bufferBinding))
{
continue;
}
// Transform feedback buffer bindings are tracked in TransformFeedbackGL
if (bufferBinding == gl::BufferBinding::TransformFeedback)
{
continue;
}
nativegl::BufferBindingQuery queryEnums = nativegl::GetBufferBindingQuery(bufferBinding);
GetHelper(functions, queryEnums.bindingQuery, &state->buffers[bufferBinding]);
for (GLuint i = 0; i < state->indexedBuffers[bufferBinding].size(); i++)
{
IndexedBufferBindingGL &binding = state->indexedBuffers[bufferBinding][i];
GetHelper(functions, queryEnums.bindingQuery, i, &binding.buffer);
ASSERT(queryEnums.startQuery.has_value());
GetHelper(functions, queryEnums.startQuery.value(), i, &binding.offset);
ASSERT(queryEnums.sizeQuery.has_value());
GetHelper(functions, queryEnums.sizeQuery.value(), i, &binding.size);
}
}
GLuint activeTexture = 0;
GetHelper(functions, GL_ACTIVE_TEXTURE, &activeTexture);
state->textureUnitIndex = activeTexture - GL_TEXTURE0;
GLint maxCombinedTextureImageUnits = 0;
GetHelper(functions, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxCombinedTextureImageUnits);
maxCombinedTextureImageUnits = std::min(
maxCombinedTextureImageUnits, static_cast<GLint>(gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES));
for (GLint i = 0; i < maxCombinedTextureImageUnits; i++)
{
functions->activeTexture(GL_TEXTURE0 + i);
for (gl::TextureType textureType : angle::AllEnums<gl::TextureType>())
{
if (!nativegl::SupportsTextureType(functions, textureType))
{
continue;
}
GetHelper(functions, nativegl::GetTextureBindingQuery(textureType),
&state->textures[textureType][i]);
}
if (nativegl::SupportsSamplerObjects(functions))
{
GetHelper(functions, GL_SAMPLER_BINDING, &state->samplers[i]);
}
}
// Reset the active texture
functions->activeTexture(activeTexture);
for (GLuint i = 0; i < state->images.size(); i++)
{
ImageUnitBindingGL &image = state->images[i];
GetHelper(functions, GL_IMAGE_BINDING_NAME, i, &image.texture);
GetHelper(functions, GL_IMAGE_BINDING_LEVEL, i, &image.level);
GetHelper(functions, GL_IMAGE_BINDING_LAYERED, i, &image.layered);
GetHelper(functions, GL_IMAGE_BINDING_LAYER, i, &image.layer);
GetHelper(functions, GL_IMAGE_BINDING_ACCESS, i, &image.access);
GetHelper(functions, GL_IMAGE_BINDING_FORMAT, i, &image.format);
}
if (nativegl::SupportsTransformFeedback(functions))
{
GetHelper(functions, GL_TRANSFORM_FEEDBACK_BINDING, &state->transformFeedback);
}
GetHelper(functions, GL_UNPACK_ALIGNMENT, &state->unpackState.alignment);
if (nativegl::SupportsUnpackSubImage(functions))
{
GetHelper(functions, GL_UNPACK_ROW_LENGTH, &state->unpackState.rowLength);
GetHelper(functions, GL_UNPACK_SKIP_ROWS, &state->unpackState.skipRows);
GetHelper(functions, GL_UNPACK_SKIP_PIXELS, &state->unpackState.skipPixels);
}
if (nativegl::Supports3DUnpackParameters(functions))
{
GetHelper(functions, GL_UNPACK_IMAGE_HEIGHT, &state->unpackState.imageHeight);
GetHelper(functions, GL_UNPACK_SKIP_IMAGES, &state->unpackState.skipImages);
}
GetHelper(functions, GL_PACK_ALIGNMENT, &state->packState.alignment);
if (nativegl::SupportsPackSubImage(functions))
{
GetHelper(functions, GL_PACK_ROW_LENGTH, &state->packState.rowLength);
GetHelper(functions, GL_PACK_SKIP_ROWS, &state->packState.skipRows);
GetHelper(functions, GL_PACK_SKIP_PIXELS, &state->packState.skipPixels);
}
if (nativegl::SupportsSeparateFramebufferBindings(functions))
{
GetHelper(functions, GL_READ_FRAMEBUFFER_BINDING,
&state->framebuffers[angle::FramebufferBindingRead]);
GetHelper(functions, GL_DRAW_FRAMEBUFFER_BINDING,
&state->framebuffers[angle::FramebufferBindingDraw]);
}
else
{
GetHelper(functions, GL_FRAMEBUFFER_BINDING,
&state->framebuffers[angle::FramebufferBindingDraw]);
state->framebuffers[angle::FramebufferBindingRead] =
state->framebuffers[angle::FramebufferBindingDraw];
}
// Bind a framebuffer with stencil bits for the rest of the queries. Many queries related to
// stencil will mask with the number of bits in the current stencil buffer.
ScopedBindDrawFramebuffer scopedFramebufferWithStencil(
functions, state->framebuffers[angle::FramebufferBindingDraw], framebufferWithStencilBits);
GetHelper(functions, GL_RENDERBUFFER_BINDING, &state->renderbuffer);
GetEnabledHelper(functions, GL_SCISSOR_TEST, &state->scissorTestEnabled);
GetHelper(functions, GL_SCISSOR_BOX, &state->scissor);
GetHelper(functions, GL_VIEWPORT, &state->viewport);
{
float depthRange[2] = {state->near, state->far};
GetHelper(functions, GL_DEPTH_RANGE, depthRange);
state->near = depthRange[0];
state->far = depthRange[1];
}
if (nativegl::SupportsClipControl(functions))
{
GetHelper(functions, GL_CLIP_ORIGIN, &state->clipOrigin);
GetHelper(functions, GL_CLIP_DEPTH_MODE, &state->clipDepthMode);
}
GetHelper(functions, GL_BLEND_COLOR, &state->blendColor);
if (nativegl::SupportsDrawBuffersIndexed(functions))
{
GLint maxDrawBuffers = 0;
GetHelper(functions, GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
maxDrawBuffers =
std::min(maxDrawBuffers, static_cast<GLint>(gl::IMPLEMENTATION_MAX_DRAW_BUFFERS));
for (GLuint i = 0; i < static_cast<GLuint>(maxDrawBuffers); i++)
{
bool enabled = state->blendState.getEnabledMask().test(i);
GetEnabledHelper(functions, GL_BLEND, i, &enabled);
state->blendState.setEnabledIndexed(i, enabled);
std::array<bool, 4> colorMask;
state->blendState.getColorMaskIndexed(i, &colorMask[0], &colorMask[1], &colorMask[2],
&colorMask[3]);
GetHelper(functions, GL_COLOR_WRITEMASK, i, &colorMask);
state->blendState.setColorMaskIndexed(i, colorMask[0], colorMask[1], colorMask[2],
colorMask[3]);
gl::BlendFactorType blendSrcRgb = state->blendState.getSrcColorIndexed(i);
GetHelper(functions, GL_BLEND_SRC_RGB, i, &blendSrcRgb);
gl::BlendFactorType blendDestRgb = state->blendState.getDstColorIndexed(i);
GetHelper(functions, GL_BLEND_DST_RGB, i, &blendDestRgb);
gl::BlendFactorType blendSrcAlpha = state->blendState.getSrcAlphaIndexed(i);
GetHelper(functions, GL_BLEND_SRC_ALPHA, i, &blendSrcAlpha);
gl::BlendFactorType blendDestAlpha = state->blendState.getDstAlphaIndexed(i);
GetHelper(functions, GL_BLEND_DST_ALPHA, i, &blendDestAlpha);
state->blendState.setFactorsIndexed(i, blendSrcRgb, blendDestRgb, blendSrcAlpha,
blendDestAlpha);
gl::BlendEquationType blendEquationRgb = state->blendState.getEquationColorIndexed(i);
GetHelper(functions, GL_BLEND_EQUATION_RGB, i, &blendEquationRgb);
gl::BlendEquationType blendEquationAlpha = state->blendState.getEquationAlphaIndexed(i);
GetHelper(functions, GL_BLEND_EQUATION_ALPHA, i, &blendEquationAlpha);
state->blendState.setEquationsIndexed(i, blendEquationRgb, blendEquationAlpha);
}
}
else
{
bool enabled = state->blendState.getEnabledMask().test(0);
GetEnabledHelper(functions, GL_BLEND, &enabled);
state->blendState.setEnabled(enabled);
std::array<bool, 4> colorMask;
state->blendState.getColorMaskIndexed(0, &colorMask[0], &colorMask[1], &colorMask[2],
&colorMask[3]);
GetHelper(functions, GL_COLOR_WRITEMASK, &colorMask);
state->blendState.setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
gl::BlendFactorType blendSrcRgb = state->blendState.getSrcColorIndexed(0);
GetHelper(functions, GL_BLEND_SRC_RGB, &blendSrcRgb);
gl::BlendFactorType blendDestRgb = state->blendState.getDstColorIndexed(0);
GetHelper(functions, GL_BLEND_DST_RGB, &blendDestRgb);
gl::BlendFactorType blendSrcAlpha = state->blendState.getSrcAlphaIndexed(0);
GetHelper(functions, GL_BLEND_SRC_ALPHA, &blendSrcAlpha);
gl::BlendFactorType blendDestAlpha = state->blendState.getDstAlphaIndexed(0);
GetHelper(functions, GL_BLEND_DST_ALPHA, &blendDestAlpha);
state->blendState.setFactors(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha);
gl::BlendEquationType blendEquationRgb = state->blendState.getEquationColorIndexed(0);
GetHelper(functions, GL_BLEND_EQUATION_RGB, &blendEquationRgb);
gl::BlendEquationType blendEquationAlpha = state->blendState.getEquationAlphaIndexed(0);
GetHelper(functions, GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha);
state->blendState.setEquations(blendEquationRgb, blendEquationAlpha);
}
if (nativegl::SupportsBlendEquationAdvancedCoherent(functions))
{
GetEnabledHelper(functions, GL_BLEND_ADVANCED_COHERENT_KHR, &state->blendAdvancedCoherent);
}
GetEnabledHelper(functions, GL_SAMPLE_ALPHA_TO_COVERAGE, &state->sampleAlphaToCoverageEnabled);
GetEnabledHelper(functions, GL_SAMPLE_COVERAGE, &state->sampleCoverageEnabled);
GetHelper(functions, GL_SAMPLE_COVERAGE_VALUE, &state->sampleCoverageValue);
GetHelper(functions, GL_SAMPLE_COVERAGE_INVERT, &state->sampleCoverageInvert);
if (nativegl::SupportsSampleMask(functions))
{
GLint maxSampleMaskWords = 0;
GetHelper(functions, GL_MAX_SAMPLE_MASK_WORDS, &maxSampleMaskWords);
maxSampleMaskWords = std::min(maxSampleMaskWords,
static_cast<GLint>(gl::IMPLEMENTATION_MAX_SAMPLE_MASK_WORDS));
GetEnabledHelper(functions, GL_SAMPLE_MASK, &state->sampleMaskEnabled);
for (GLuint i = 0; i < static_cast<GLuint>(maxSampleMaskWords); i++)
{
GetHelper(functions, GL_SAMPLE_MASK_VALUE, i, &state->sampleMaskValues[i]);
}
}
GetEnabledHelper(functions, GL_DEPTH_TEST, &state->depthTestEnabled);
GetHelper(functions, GL_DEPTH_FUNC, &state->depthFunc);
GetHelper(functions, GL_DEPTH_WRITEMASK, &state->depthMask);
GetEnabledHelper(functions, GL_STENCIL_TEST, &state->stencilTestEnabled);
GetHelper(functions, GL_STENCIL_FUNC, &state->stencilFrontFunc);
GetHelper(functions, GL_STENCIL_REF, &state->stencilFrontRef);
GetHelper(functions, GL_STENCIL_VALUE_MASK, &state->stencilFrontValueMask);
GetHelper(functions, GL_STENCIL_FAIL, &state->stencilFrontStencilFailOp);
GetHelper(functions, GL_STENCIL_PASS_DEPTH_FAIL, &state->stencilFrontStencilPassDepthFailOp);
GetHelper(functions, GL_STENCIL_PASS_DEPTH_PASS, &state->stencilFrontStencilPassDepthPassOp);
GetHelper(functions, GL_STENCIL_WRITEMASK, &state->stencilFrontWritemask);
GetHelper(functions, GL_STENCIL_BACK_FUNC, &state->stencilBackFunc);
GetHelper(functions, GL_STENCIL_BACK_REF, &state->stencilBackRef);
GetHelper(functions, GL_STENCIL_BACK_VALUE_MASK, &state->stencilBackValueMask);
GetHelper(functions, GL_STENCIL_BACK_FAIL, &state->stencilBackStencilFailOp);
GetHelper(functions, GL_STENCIL_BACK_PASS_DEPTH_FAIL,
&state->stencilBackStencilPassDepthFailOp);
GetHelper(functions, GL_STENCIL_BACK_PASS_DEPTH_PASS,
&state->stencilBackStencilPassDepthPassOp);
GetHelper(functions, GL_STENCIL_BACK_WRITEMASK, &state->stencilBackWritemask);
GetEnabledHelper(functions, GL_CULL_FACE, &state->cullFaceEnabled);
GetHelper(functions, GL_CULL_FACE_MODE, &state->cullFace);
GetHelper(functions, GL_FRONT_FACE, &state->frontFace);
if (nativegl::SupportsPolygonMode(functions))
{
// GL Core and ES contexts return one value. GL Compatibility contexts return two values.
std::array<gl::PolygonMode, 2> polygonMode = {state->polygonMode, state->polygonMode};
GetHelper(functions, GL_POLYGON_MODE, &polygonMode);
// Check that either the two values are equal or the second one is unwritten.
ASSERT(polygonMode[0] == polygonMode[1] || polygonMode[1] == state->polygonMode);
state->polygonMode = polygonMode[0];
GetEnabledHelper(functions, GL_POLYGON_OFFSET_POINT, &state->polygonOffsetPointEnabled);
GetEnabledHelper(functions, GL_POLYGON_OFFSET_LINE, &state->polygonOffsetLineEnabled);
}
GetEnabledHelper(functions, GL_POLYGON_OFFSET_FILL, &state->polygonOffsetFillEnabled);
GetHelper(functions, GL_POLYGON_OFFSET_FACTOR, &state->polygonOffsetFactor);
GetHelper(functions, GL_POLYGON_OFFSET_UNITS, &state->polygonOffsetUnits);
if (nativegl::SupportsPolygonOffsetClamp(functions))
{
GetHelper(functions, GL_POLYGON_OFFSET_CLAMP_EXT, &state->polygonOffsetClamp);
}
if (nativegl::SupportsDepthClamp(functions))
{
GetEnabledHelper(functions, GL_DEPTH_CLAMP_EXT, &state->depthClampEnabled);
}
if (nativegl::SupportsRasterizerDiscard(functions))
{
GetEnabledHelper(functions, GL_RASTERIZER_DISCARD, &state->rasterizerDiscardEnabled);
}
GetHelper(functions, GL_LINE_WIDTH, &state->lineWidth);
if (nativegl::SupportsPrimitiveRestartFixedIndex(functions))
{
GetEnabledHelper(functions, GL_PRIMITIVE_RESTART_FIXED_INDEX,
&state->primitiveRestartFixedIndexEnabled);
}
if (nativegl::SupportsPrimitiveRestart(functions))
{
GetEnabledHelper(functions, GL_PRIMITIVE_RESTART, &state->primitiveRestartEnabled);
GetHelper(functions, GL_PRIMITIVE_RESTART_INDEX, &state->primitiveRestartIndex);
}
GetHelper(functions, GL_COLOR_CLEAR_VALUE, &state->clearColor);
GetHelper(functions, GL_DEPTH_CLEAR_VALUE, &state->clearDepth);
GetHelper(functions, GL_STENCIL_CLEAR_VALUE, &state->clearStencil);
if (nativegl::SupportsSRGBWriteControl(functions))
{
GetEnabledHelper(functions, GL_FRAMEBUFFER_SRGB, &state->framebufferSRGBEnabled);
}
GetHelper(functions, GL_DITHER, &state->ditherEnabled);
if (nativegl::SupportsSettingCubemapSeamless(functions))
{
GetEnabledHelper(functions, GL_TEXTURE_CUBE_MAP_SEAMLESS,
&state->textureCubemapSeamlessEnabled);
}
if (nativegl::SupportsMultisampleComatibility(functions))
{
GetEnabledHelper(functions, GL_MULTISAMPLE, &state->multisamplingEnabled);
GetHelper(functions, GL_SAMPLE_ALPHA_TO_ONE, &state->sampleAlphaToOneEnabled);
}
if (nativegl::SupportsProvokingVertex(functions))
{
GetHelper(functions, GL_PROVOKING_VERTEX, &state->provokingVertex);
}
if (nativegl::SupportsClipCullDistance(functions))
{
GLint maxClipDistances = 0;
GetHelper(functions, GL_MAX_CLIP_DISTANCES, &maxClipDistances);
maxClipDistances =
std::min(maxClipDistances, static_cast<GLint>(gl::IMPLEMENTATION_MAX_CLIP_DISTANCES));
for (GLuint i = 0; i < static_cast<GLuint>(maxClipDistances); i++)
{
bool enabled = state->enabledClipDistances.test(i);
GetEnabledHelper(functions, GL_CLIP_DISTANCE0 + i, &enabled);
state->enabledClipDistances[i] = enabled;
}
}
if (nativegl::SupportsLogicOp(functions))
{
GetEnabledHelper(functions, GL_COLOR_LOGIC_OP, &state->logicOpEnabled);
GetHelper(functions, GL_LOGIC_OP_MODE, &state->logicOp);
}
}
bool VertexAttribCurrentValuesEqual(const gl::VertexAttribCurrentValueData &a,
const gl::VertexAttribCurrentValueData &b)
{
// When comparing vertex attribute current values, only compare the data, not the type.
return ANGLE_UNSAFE_BUFFERS(
memcmp(&a.Values, &b.Values, sizeof(gl::VertexAttribCurrentValueData::Values))) == 0;
}
// Compute the mask of attributes that have different current values
gl::AttributesMask ComputeVertexAttribCurrentValueDiffMask(
const gl::AttribArray<gl::VertexAttribCurrentValueData> &a,
const gl::AttribArray<gl::VertexAttribCurrentValueData> &b)
{
gl::AttributesMask diffMask;
for (size_t i = 0; i < a.size(); i++)
{
diffMask.set(i, !VertexAttribCurrentValuesEqual(a[i], b[i]));
}
return diffMask;
}
std::optional<gl::state::DirtyBitType> GetBufferBindingDirtyBit(gl::BufferBinding binding)
{
switch (binding)
{
case gl::BufferBinding::Array:
// Array buffer bindings are set before vertex attrib calls.
return std::nullopt;
case gl::BufferBinding::AtomicCounter:
return gl::state::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING;
case gl::BufferBinding::CopyRead:
// CopyRead does not affect any operations.
return std::nullopt;
case gl::BufferBinding::CopyWrite:
// CopyWrite does not affect any operations.
return std::nullopt;
case gl::BufferBinding::DispatchIndirect:
return gl::state::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING;
case gl::BufferBinding::DrawIndirect:
return gl::state::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING;
case gl::BufferBinding::ElementArray:
// Managed by the VAO
return std::nullopt;
case gl::BufferBinding::PixelPack:
return gl::state::DIRTY_BIT_PACK_BUFFER_BINDING;
case gl::BufferBinding::PixelUnpack:
return gl::state::DIRTY_BIT_UNPACK_BUFFER_BINDING;
case gl::BufferBinding::ShaderStorage:
return gl::state::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING;
case gl::BufferBinding::Texture:
// Not implemented in the GL backend
return std::nullopt;
case gl::BufferBinding::TransformFeedback:
// Transform feedback buffer bindings are tracked in TransformFeedbackGL
return std::nullopt;
case gl::BufferBinding::Uniform:
return gl::state::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS;
default:
UNREACHABLE();
return std::nullopt;
}
}
template <typename T>
void SetDirtyBitIfStateDifferent(const T &a,
const T &b,
gl::state::DirtyBits *bits,
gl::state::DirtyBitType bit)
{
if (a != b)
{
bits->set(bit);
}
}
template <typename T>
void SetDirtyBitExtIfStateDifferent(const T &a,
const T &b,
gl::state::DirtyBits *bits,
gl::state::ExtendedDirtyBits *exBits,
gl::state::ExtendedDirtyBitType exBit)
{
if (a != b)
{
bits->set(gl::state::DIRTY_BIT_EXTENDED);
exBits->set(exBit);
}
}
// Compute the set of dirty bits needed to transition between two ContextStateGL objects.
[[maybe_unused]] void ComputeDirtyBitsBetweenStates(const ContextStateGL &a,
const ContextStateGL &b,
gl::state::DirtyBits *bits,
gl::state::ExtendedDirtyBits *exBits,
gl::AttributesMask *dirtyCurrentValues)
{
SetDirtyBitIfStateDifferent(a.program, b.program, bits, gl::state::DIRTY_BIT_PROGRAM_BINDING);
SetDirtyBitIfStateDifferent(a.vao, b.vao, bits, gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING);
SetDirtyBitIfStateDifferent(a.defaultVAOState, b.defaultVAOState, bits,
gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING);
{
gl::AttributesMask currentValueDiff = ComputeVertexAttribCurrentValueDiffMask(
a.vertexAttribCurrentValues, b.vertexAttribCurrentValues);
if (currentValueDiff.any())
{
*dirtyCurrentValues |= currentValueDiff;
bits->set(gl::state::DIRTY_BIT_CURRENT_VALUES);
}
}
for (gl::BufferBinding bufferBinding : angle::AllEnums<gl::BufferBinding>())
{
std::optional<gl::state::DirtyBitType> bit = GetBufferBindingDirtyBit(bufferBinding);
if (!bit.has_value())
{
continue;
}
SetDirtyBitIfStateDifferent(a.buffers[bufferBinding], b.buffers[bufferBinding], bits,
bit.value());
SetDirtyBitIfStateDifferent(a.indexedBuffers[bufferBinding],
b.indexedBuffers[bufferBinding], bits, bit.value());
}
// No dirty bit for ContextStateGL::activeTexture
SetDirtyBitIfStateDifferent(a.textures, b.textures, bits,
gl::state::DIRTY_BIT_TEXTURE_BINDINGS);
SetDirtyBitIfStateDifferent(a.samplers, b.samplers, bits,
gl::state::DIRTY_BIT_SAMPLER_BINDINGS);
SetDirtyBitIfStateDifferent(a.images, b.images, bits, gl::state::DIRTY_BIT_IMAGE_BINDINGS);
SetDirtyBitIfStateDifferent(a.transformFeedback, b.transformFeedback, bits,
gl::state::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING);
SetDirtyBitIfStateDifferent(a.unpackState, b.unpackState, bits,
gl::state::DIRTY_BIT_UNPACK_STATE);
SetDirtyBitIfStateDifferent(a.packState, b.packState, bits, gl::state::DIRTY_BIT_PACK_STATE);
SetDirtyBitIfStateDifferent(a.framebuffers[angle::FramebufferBindingRead],
b.framebuffers[angle::FramebufferBindingRead], bits,
gl::state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
SetDirtyBitIfStateDifferent(a.framebuffers[angle::FramebufferBindingDraw],
b.framebuffers[angle::FramebufferBindingDraw], bits,
gl::state::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING);
SetDirtyBitIfStateDifferent(a.renderbuffer, b.renderbuffer, bits,
gl::state::DIRTY_BIT_RENDERBUFFER_BINDING);
SetDirtyBitIfStateDifferent(a.scissorTestEnabled, b.scissorTestEnabled, bits,
gl::state::DIRTY_BIT_SCISSOR_TEST_ENABLED);
SetDirtyBitIfStateDifferent(a.scissor, b.scissor, bits, gl::state::DIRTY_BIT_SCISSOR);
SetDirtyBitIfStateDifferent(a.viewport, b.viewport, bits, gl::state::DIRTY_BIT_VIEWPORT);
SetDirtyBitIfStateDifferent(a.near, b.near, bits, gl::state::DIRTY_BIT_DEPTH_RANGE);
SetDirtyBitIfStateDifferent(a.far, b.far, bits, gl::state::DIRTY_BIT_DEPTH_RANGE);
SetDirtyBitIfStateDifferent(a.clipOrigin, b.clipOrigin, bits,
gl::state::DIRTY_BIT_CLIP_CONTROL);
SetDirtyBitIfStateDifferent(a.clipDepthMode, b.clipDepthMode, bits,
gl::state::DIRTY_BIT_CLIP_CONTROL);
SetDirtyBitIfStateDifferent(a.blendColor, b.blendColor, bits, gl::state::DIRTY_BIT_BLEND_COLOR);
SetDirtyBitIfStateDifferent(a.blendState.getAllEnabledMask(), b.blendState.getAllEnabledMask(),
bits, gl::state::DIRTY_BIT_COLOR_MASK);
SetDirtyBitIfStateDifferent(a.blendState.getColorMaskBits(), b.blendState.getColorMaskBits(),
bits, gl::state::DIRTY_BIT_BLEND_ENABLED);
SetDirtyBitIfStateDifferent(a.blendState.getSrcColorBits(), b.blendState.getSrcColorBits(),
bits, gl::state::DIRTY_BIT_BLEND_FUNCS);
SetDirtyBitIfStateDifferent(a.blendState.getDstColorBits(), b.blendState.getDstColorBits(),
bits, gl::state::DIRTY_BIT_BLEND_FUNCS);
SetDirtyBitIfStateDifferent(a.blendState.getSrcAlphaBits(), b.blendState.getSrcAlphaBits(),
bits, gl::state::DIRTY_BIT_BLEND_FUNCS);
SetDirtyBitIfStateDifferent(a.blendState.getDstAlphaBits(), b.blendState.getDstAlphaBits(),
bits, gl::state::DIRTY_BIT_BLEND_FUNCS);
SetDirtyBitIfStateDifferent(a.blendState.getEquationColorBits(),
b.blendState.getEquationColorBits(), bits,
gl::state::DIRTY_BIT_BLEND_EQUATIONS);
SetDirtyBitIfStateDifferent(a.blendState.getEquationAlphaBits(),
b.blendState.getEquationAlphaBits(), bits,
gl::state::DIRTY_BIT_BLEND_EQUATIONS);
SetDirtyBitExtIfStateDifferent(a.blendAdvancedCoherent, b.blendAdvancedCoherent, bits, exBits,
gl::state::EXTENDED_DIRTY_BIT_BLEND_ADVANCED_COHERENT);
SetDirtyBitIfStateDifferent(a.sampleAlphaToCoverageEnabled, b.sampleAlphaToCoverageEnabled,
bits, gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED);
SetDirtyBitIfStateDifferent(a.sampleCoverageEnabled, b.sampleCoverageEnabled, bits,
gl::state::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED);
SetDirtyBitIfStateDifferent(a.sampleCoverageValue, b.sampleCoverageValue, bits,
gl::state::DIRTY_BIT_SAMPLE_COVERAGE);
SetDirtyBitIfStateDifferent(a.sampleCoverageInvert, b.sampleCoverageInvert, bits,
gl::state::DIRTY_BIT_SAMPLE_COVERAGE);
SetDirtyBitIfStateDifferent(a.sampleMaskEnabled, b.sampleMaskEnabled, bits,
gl::state::DIRTY_BIT_SAMPLE_MASK_ENABLED);
SetDirtyBitIfStateDifferent(a.sampleMaskValues, b.sampleMaskValues, bits,
gl::state::DIRTY_BIT_SAMPLE_MASK);
SetDirtyBitIfStateDifferent(a.depthTestEnabled, b.depthTestEnabled, bits,
gl::state::DIRTY_BIT_DEPTH_TEST_ENABLED);
SetDirtyBitIfStateDifferent(a.depthFunc, b.depthFunc, bits, gl::state::DIRTY_BIT_DEPTH_FUNC);
SetDirtyBitIfStateDifferent(a.depthMask, b.depthMask, bits, gl::state::DIRTY_BIT_DEPTH_MASK);
SetDirtyBitIfStateDifferent(a.stencilTestEnabled, b.stencilTestEnabled, bits,
gl::state::DIRTY_BIT_STENCIL_TEST_ENABLED);
SetDirtyBitIfStateDifferent(a.stencilFrontFunc, b.stencilFrontFunc, bits,
gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT);
SetDirtyBitIfStateDifferent(a.stencilFrontRef, b.stencilFrontRef, bits,
gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT);
SetDirtyBitIfStateDifferent(a.stencilFrontValueMask, b.stencilFrontValueMask, bits,
gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT);
SetDirtyBitIfStateDifferent(a.stencilFrontStencilFailOp, b.stencilFrontStencilFailOp, bits,
gl::state::DIRTY_BIT_STENCIL_OPS_FRONT);
SetDirtyBitIfStateDifferent(a.stencilFrontStencilPassDepthFailOp,
b.stencilFrontStencilPassDepthFailOp, bits,
gl::state::DIRTY_BIT_STENCIL_OPS_FRONT);
SetDirtyBitIfStateDifferent(a.stencilFrontStencilPassDepthPassOp,
b.stencilFrontStencilPassDepthPassOp, bits,
gl::state::DIRTY_BIT_STENCIL_OPS_FRONT);
SetDirtyBitIfStateDifferent(a.stencilFrontWritemask, b.stencilFrontWritemask, bits,
gl::state::DIRTY_BIT_STENCIL_WRITEMASK_FRONT);
SetDirtyBitIfStateDifferent(a.stencilBackFunc, b.stencilBackFunc, bits,
gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK);
SetDirtyBitIfStateDifferent(a.stencilBackRef, b.stencilBackRef, bits,
gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK);
SetDirtyBitIfStateDifferent(a.stencilBackValueMask, b.stencilBackValueMask, bits,
gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK);
SetDirtyBitIfStateDifferent(a.stencilBackStencilFailOp, b.stencilBackStencilFailOp, bits,
gl::state::DIRTY_BIT_STENCIL_OPS_BACK);
SetDirtyBitIfStateDifferent(a.stencilBackStencilPassDepthFailOp,
b.stencilBackStencilPassDepthFailOp, bits,
gl::state::DIRTY_BIT_STENCIL_OPS_BACK);
SetDirtyBitIfStateDifferent(a.stencilBackStencilPassDepthPassOp,
b.stencilBackStencilPassDepthPassOp, bits,
gl::state::DIRTY_BIT_STENCIL_OPS_BACK);
SetDirtyBitIfStateDifferent(a.stencilBackWritemask, b.stencilBackWritemask, bits,
gl::state::DIRTY_BIT_STENCIL_WRITEMASK_BACK);
SetDirtyBitIfStateDifferent(a.cullFaceEnabled, b.cullFaceEnabled, bits,
gl::state::DIRTY_BIT_CULL_FACE_ENABLED);
SetDirtyBitIfStateDifferent(a.cullFace, b.cullFace, bits, gl::state::DIRTY_BIT_CULL_FACE);
SetDirtyBitIfStateDifferent(a.frontFace, b.frontFace, bits, gl::state::DIRTY_BIT_FRONT_FACE);
SetDirtyBitExtIfStateDifferent(a.polygonMode, b.polygonMode, bits, exBits,
gl::state::EXTENDED_DIRTY_BIT_POLYGON_MODE);
SetDirtyBitExtIfStateDifferent(a.polygonOffsetPointEnabled, b.polygonOffsetPointEnabled, bits,
exBits,
gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_POINT_ENABLED);
SetDirtyBitExtIfStateDifferent(a.polygonOffsetLineEnabled, b.polygonOffsetLineEnabled, bits,
exBits,
gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_LINE_ENABLED);
SetDirtyBitIfStateDifferent(a.polygonOffsetFillEnabled, b.polygonOffsetFillEnabled, bits,
gl::state::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED);
SetDirtyBitIfStateDifferent(a.polygonOffsetFactor, b.polygonOffsetFactor, bits,
gl::state::DIRTY_BIT_POLYGON_OFFSET);
SetDirtyBitIfStateDifferent(a.polygonOffsetUnits, b.polygonOffsetUnits, bits,
gl::state::DIRTY_BIT_POLYGON_OFFSET);
SetDirtyBitIfStateDifferent(a.polygonOffsetClamp, b.polygonOffsetClamp, bits,
gl::state::DIRTY_BIT_POLYGON_OFFSET);
SetDirtyBitExtIfStateDifferent(a.depthClampEnabled, b.depthClampEnabled, bits, exBits,
gl::state::EXTENDED_DIRTY_BIT_DEPTH_CLAMP_ENABLED);
SetDirtyBitIfStateDifferent(a.rasterizerDiscardEnabled, b.rasterizerDiscardEnabled, bits,
gl::state::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED);
SetDirtyBitIfStateDifferent(a.lineWidth, b.lineWidth, bits, gl::state::DIRTY_BIT_LINE_WIDTH);
SetDirtyBitIfStateDifferent(a.primitiveRestartFixedIndexEnabled,
b.primitiveRestartFixedIndexEnabled, bits,
gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED);
SetDirtyBitIfStateDifferent(a.primitiveRestartEnabled, b.primitiveRestartEnabled, bits,
gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED);
SetDirtyBitIfStateDifferent(a.primitiveRestartIndex, b.primitiveRestartIndex, bits,
gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED);
SetDirtyBitIfStateDifferent(a.clearColor, b.clearColor, bits, gl::state::DIRTY_BIT_CLEAR_COLOR);
SetDirtyBitIfStateDifferent(a.clearDepth, b.clearDepth, bits, gl::state::DIRTY_BIT_CLEAR_DEPTH);
SetDirtyBitIfStateDifferent(a.clearStencil, b.clearStencil, bits,
gl::state::DIRTY_BIT_CLEAR_STENCIL);
SetDirtyBitIfStateDifferent(a.framebufferSRGBEnabled, b.framebufferSRGBEnabled, bits,
gl::state::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE);
SetDirtyBitIfStateDifferent(a.ditherEnabled, b.ditherEnabled, bits,
gl::state::DIRTY_BIT_DITHER_ENABLED);
// No dirty bit for ContextStateGL::textureCubemapSeamlessEnabled
SetDirtyBitIfStateDifferent(a.multisamplingEnabled, b.multisamplingEnabled, bits,
gl::state::DIRTY_BIT_MULTISAMPLING);
SetDirtyBitIfStateDifferent(a.sampleAlphaToOneEnabled, b.sampleAlphaToOneEnabled, bits,
gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE);
SetDirtyBitIfStateDifferent(a.provokingVertex, b.provokingVertex, bits,
gl::state::DIRTY_BIT_PROVOKING_VERTEX);
SetDirtyBitExtIfStateDifferent(a.enabledClipDistances, b.enabledClipDistances, bits, exBits,
gl::state::EXTENDED_DIRTY_BIT_CLIP_DISTANCES);
SetDirtyBitExtIfStateDifferent(a.logicOpEnabled, b.logicOpEnabled, bits, exBits,
gl::state::EXTENDED_DIRTY_BIT_LOGIC_OP_ENABLED);
SetDirtyBitExtIfStateDifferent(a.logicOp, b.logicOp, bits, exBits,
gl::state::EXTENDED_DIRTY_BIT_LOGIC_OP);
}
auto TieIndexedBufferBindingGL(const IndexedBufferBindingGL &binding)
{
return std::tie(binding.offset, binding.size, binding.buffer);
}
auto TieImageUnitBindingGL(const ImageUnitBindingGL &binding)
{
return std::tie(binding.texture, binding.level, binding.layered, binding.layer, binding.access,
binding.format);
}
auto TieContextStateGL(const ContextStateGL &state)
{
// state.vertexAttribCurrentValues is omitted and handled specially in the comparison operator
// state.Stencil(Front|Back)(WriteMask|ValueMask) is omitted and handled specifically in the
// comparison operator because it must be masked
return std::tie(
state.program, state.vao, state.defaultVAOState,
/*state.vertexAttribCurrentValues,*/ state.buffers, state.indexedBuffers,
state.textureUnitIndex, state.textures, state.samplers, state.images,
state.transformFeedback, state.unpackState, state.packState, state.framebuffers,
state.renderbuffer, state.scissorTestEnabled, state.scissor, state.viewport, state.near,
state.far, state.clipOrigin, state.clipDepthMode, state.blendColor, state.blendState,
state.blendAdvancedCoherent, state.sampleAlphaToCoverageEnabled,
state.sampleCoverageEnabled, state.sampleCoverageValue, state.sampleCoverageInvert,
state.sampleMaskEnabled, state.sampleMaskValues, state.depthTestEnabled, state.depthFunc,
state.depthMask, state.stencilTestEnabled, state.stencilFrontFunc, state.stencilFrontRef,
/*state.stencilFrontValueMask,*/ state.stencilFrontStencilFailOp,
state.stencilFrontStencilPassDepthFailOp, state.stencilFrontStencilPassDepthPassOp,
/*state.stencilFrontWritemask,*/ state.stencilBackFunc, state.stencilBackRef,
/*state.stencilBackValueMask,*/ state.stencilBackStencilFailOp,
state.stencilBackStencilPassDepthFailOp, state.stencilBackStencilPassDepthPassOp,
/*state.stencilBackWritemask,*/ state.cullFaceEnabled, state.cullFace, state.frontFace,
state.polygonMode, state.polygonOffsetPointEnabled, state.polygonOffsetLineEnabled,
state.polygonOffsetFillEnabled, state.polygonOffsetFactor, state.polygonOffsetUnits,
state.polygonOffsetClamp, state.depthClampEnabled, state.rasterizerDiscardEnabled,
state.lineWidth, state.primitiveRestartFixedIndexEnabled, state.primitiveRestartEnabled,
state.primitiveRestartIndex, state.clearColor, state.clearDepth, state.clearStencil,
state.framebufferSRGBEnabled, state.ditherEnabled, state.textureCubemapSeamlessEnabled,
state.multisamplingEnabled, state.sampleAlphaToOneEnabled, state.provokingVertex,
state.enabledClipDistances, state.logicOpEnabled, state.logicOp);
}
void Indent(std::ostream &os, size_t count)
{
for (size_t indent = 0; indent < count; indent++)
{
os << " ";
}
}
template <typename T>
void PrintCompressedArray(std::ostream &os,
const T &values,
size_t indentation,
bool wrapValueInParens)
{
for (size_t i = 0; i < values.size(); i++)
{
size_t start = i;
while (i + 1 < values.size() && values[i + 1] == values[i])
{
i++;
}
Indent(os, indentation);
os << "[" << start;
if (i > start)
{
os << ".." << i;
}
os << "] = ";
if (wrapValueInParens)
{
os << "(";
}
os << values[i];
if (wrapValueInParens)
{
os << ")";
}
os << std::endl;
}
}
std::string PrintIndexedBlendState(const gl::BlendStateExt &blendState, size_t index)
{
std::ostringstream os;
os << "enabled = " << blendState.getEnabledMask().test(index) << ", ";
bool r, g, b, a;
blendState.getColorMaskIndexed(index, &r, &g, &b, &a);
os << "mask = " << (r ? "R" : "_") << (g ? "G" : "_") << (b ? "B" : "_") << (a ? "A" : "_")
<< ",";
os << "colorMode = " << blendState.getEquationColorIndexed(index) << ", ";
os << "alphaMode = " << blendState.getEquationAlphaIndexed(index) << ", ";
os << "srcColor = " << blendState.getSrcColorIndexed(index) << ", ";
os << "dstColor = " << blendState.getDstColorIndexed(index) << ", ";
os << "srcAlpha = " << blendState.getSrcAlphaIndexed(index) << ", ";
os << "dstAlpha = " << blendState.getDstAlphaIndexed(index);
return os.str();
}
void PrintCompressedBlendState(std::ostream &os,
const gl::BlendStateExt &blendState,
size_t indentation)
{
for (size_t i = 0; i < blendState.getDrawBufferCount(); i++)
{
std::string printed = PrintIndexedBlendState(blendState, i);
size_t start = i;
while (i + 1 < blendState.getDrawBufferCount() &&
PrintIndexedBlendState(blendState, i + 1) == printed)
{
i++;
}
Indent(os, indentation);
os << "[" << start;
if (i > start)
{
os << ".." << i;
}
os << "] = (" << printed << ")" << std::endl;
}
}
void PrintCompressedVertexArrayState(std::ostream &os,
const VertexArrayStateGL &state,
size_t indentation)
{
Indent(os, indentation);
os << "elementArrayBuffer = " << state.elementArrayBuffer << std::endl;
Indent(os, indentation);
os << "attributes =" << std::endl;
PrintCompressedArray(os, state.attributes, indentation + 4, true);
Indent(os, indentation);
os << "bindings =" << std::endl;
PrintCompressedArray(os, state.bindings, indentation + 4, true);
}
} // anonymous namespace
std::ostream &operator<<(std::ostream &os, const VertexAttributeGL &attribute)
{
os << "enabled = " << attribute.enabled << ", size = " << attribute.format->channelCount
<< ", type = " << attribute.format->vertexAttribType
<< ", normalized = " << attribute.format->isNorm() << ", pointer = " << attribute.pointer
<< ", relativeOffset = " << attribute.relativeOffset
<< ", bindingIndex = " << attribute.bindingIndex;
return os;
}
std::ostream &operator<<(std::ostream &os, const VertexBindingGL &binding)
{
os << "stride = " << binding.stride << ", divisor = " << binding.divisor
<< ", offset = " << binding.offset << ", buffer = " << binding.buffer;
return os;
}
VertexArrayStateGL::VertexArrayStateGL(size_t maxAttribs, size_t maxBindings)
: attributes(std::min<size_t>(maxAttribs, gl::MAX_VERTEX_ATTRIBS)),
bindings(std::min<size_t>(maxBindings, gl::MAX_VERTEX_ATTRIBS))
{
// Set the cached vertex attribute array and vertex attribute binding array size
for (GLuint i = 0; i < attributes.size(); i++)
{
attributes[i].bindingIndex = i;
}
}
std::ostream &operator<<(std::ostream &os, const VertexArrayStateGL &state)
{
PrintCompressedVertexArrayState(os, state, 0);
return os;
}
void QueryVertexArrayStateGL(const FunctionsGL *functions, VertexArrayStateGL *state)
{
GetHelper(functions, GL_ELEMENT_ARRAY_BUFFER_BINDING, &state->elementArrayBuffer);
GLint maxVertexAttribs = 0;
GetHelper(functions, GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
maxVertexAttribs = std::min(maxVertexAttribs, static_cast<GLint>(gl::MAX_VERTEX_ATTRIBS));
for (GLint i = 0; i < maxVertexAttribs; i++)
{
VertexAttributeGL &attrib = state->attributes[i];
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &attrib.enabled);
GLuint size = attrib.format->channelCount;
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size);
gl::VertexAttribType type = attrib.format->vertexAttribType;
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &type);
bool normalized = attrib.format->isNorm();
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized);
bool integer = attrib.format->isPureInt();
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_INTEGER, &integer);
attrib.format = &angle::Format::Get(gl::GetVertexFormatID(type, normalized, size, integer));
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_POINTER,
const_cast<void **>(&attrib.pointer));
if (nativegl::SupportsVertexAttributeBindings(functions))
{
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_RELATIVE_OFFSET, &attrib.relativeOffset);
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_BINDING, &attrib.bindingIndex);
}
else
{
ASSERT(attrib.bindingIndex == static_cast<GLuint>(i));
}
}
GLint maxVertexAttribBindings = maxVertexAttribs;
if (nativegl::SupportsVertexAttributeBindings(functions))
{
GetHelper(functions, GL_MAX_VERTEX_ATTRIB_BINDINGS, &maxVertexAttribBindings);
maxVertexAttribBindings =
std::min(maxVertexAttribBindings, static_cast<GLint>(gl::MAX_VERTEX_ATTRIB_BINDINGS));
}
const bool supportsInstancing = nativegl::SupportsInstancing(functions);
for (GLint i = 0; i < maxVertexAttribBindings; i++)
{
VertexBindingGL &binding = state->bindings[i];
if (nativegl::SupportsVertexAttributeBindings(functions))
{
GetHelper(functions, GL_VERTEX_BINDING_STRIDE, i, &binding.stride);
ASSERT(supportsInstancing);
GetHelper(functions, GL_VERTEX_BINDING_DIVISOR, i, &binding.divisor);
GetHelper(functions, GL_VERTEX_BINDING_OFFSET, i, &binding.offset);
GetHelper(functions, GL_VERTEX_BINDING_BUFFER, i, &binding.buffer);
}
else
{
const VertexAttributeGL &attrib = state->attributes[i];
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &binding.stride);
if (supportsInstancing)
{
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, &binding.divisor);
}
binding.offset = reinterpret_cast<GLintptr>(attrib.pointer);
GetVertexHelper(functions, i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &binding.buffer);
}
// Driver return inconsistent results when querying the binding offset when client data
// is used. The spec says glVertexAttribPointer on the default VAO with no buffer bound
// should set an offset of 0.
if (binding.buffer == 0)
{
binding.offset = 0;
}
}
}
bool operator==(const IndexedBufferBindingGL &a, const IndexedBufferBindingGL &b)
{
return TieIndexedBufferBindingGL(a) == TieIndexedBufferBindingGL(b);
}
std::ostream &operator<<(std::ostream &os, const IndexedBufferBindingGL &binding)
{
os << "offset = " << binding.offset << ", size = " << binding.size
<< ", buffer = " << binding.buffer;
return os;
}
ImageUnitBindingGL::ImageUnitBindingGL(GLenum defaultFormat) : format(defaultFormat) {}
bool operator==(const ImageUnitBindingGL &a, const ImageUnitBindingGL &b)
{
return TieImageUnitBindingGL(a) == TieImageUnitBindingGL(b);
}
std::ostream &operator<<(std::ostream &os, const ImageUnitBindingGL &binding)
{
os << "texture = " << binding.texture << ", level = " << binding.level
<< ", layered = " << gl::ConvertToBool(binding.layered) << ", layer = " << binding.layer
<< ", access = " << gl::FmtHex(binding.access)
<< ", format = " << gl::FmtHex(binding.format);
return os;
}
ContextStateGLCaps::ContextStateGLCaps(const FunctionsGL *functions, const gl::Caps &caps)
: defaultFramebufferSrgbState(functions->standard == STANDARD_GL_ES),
defaultImageBindingFormat(functions->standard == STANDARD_GL_ES ? GL_R32UI : GL_R8),
maxVertexAttributes(
std::min(caps.maxVertexAttributes, static_cast<GLint>(gl::MAX_VERTEX_ATTRIBS))),
maxVertexAttribBindings(std::min(caps.maxVertexAttribBindings,
static_cast<GLint>(gl::MAX_VERTEX_ATTRIB_BINDINGS))),
maxImageUnits(
std::min(caps.maxImageUnits, static_cast<GLint>(gl::IMPLEMENTATION_MAX_IMAGE_UNITS))),
maxDrawBuffers(
std::min(caps.maxDrawBuffers, static_cast<GLint>(gl::IMPLEMENTATION_MAX_DRAW_BUFFERS))),
maxUniformBufferBindings(caps.maxUniformBufferBindings),
maxAtomicCounterBufferBindings(caps.maxAtomicCounterBufferBindings),
maxShaderStorageBufferBindings(caps.maxShaderStorageBufferBindings)
{}
ContextStateGL::ContextStateGL(const ContextStateGLCaps &caps)
: defaultVAOState(caps.maxVertexAttributes, caps.maxVertexAttribBindings),
images(caps.maxImageUnits, ImageUnitBindingGL(caps.defaultImageBindingFormat)),
blendState(caps.maxDrawBuffers),
framebufferSRGBEnabled(caps.defaultFramebufferSrgbState)
{
indexedBuffers[gl::BufferBinding::Uniform].resize(caps.maxUniformBufferBindings);
indexedBuffers[gl::BufferBinding::AtomicCounter].resize(caps.maxAtomicCounterBufferBindings);
indexedBuffers[gl::BufferBinding::ShaderStorage].resize(caps.maxShaderStorageBufferBindings);
sampleMaskValues.fill(~GLbitfield(0));
}
bool operator==(const ContextStateGL &a, const ContextStateGL &b)
{
if (TieContextStateGL(a) != TieContextStateGL(b))
{
return false;
}
if (ComputeVertexAttribCurrentValueDiffMask(a.vertexAttribCurrentValues,
b.vertexAttribCurrentValues)
.any())
{
return false;
}
auto makeStencilMaskTuple = [](const ContextStateGL &state) {
return std::make_tuple(
state.stencilFrontValueMask & 0xFF, state.stencilFrontWritemask & 0xFF,
state.stencilBackValueMask & 0xFF, state.stencilBackWritemask & 0xFF);
};
if (makeStencilMaskTuple(a) != makeStencilMaskTuple(b))
{
return false;
}
return true;
}
bool operator!=(const ContextStateGL &a, const ContextStateGL &b)
{
return !(a == b);
}
std::ostream &operator<<(std::ostream &os, const ContextStateGL &state)
{
os << "program = " << state.program << std::endl;
os << "vao = " << state.vao << std::endl;
os << "defaultVAOState =" << std::endl;
PrintCompressedVertexArrayState(os, state.defaultVAOState, 4);
os << "vertexAttribCurrentValues =" << std::endl;
PrintCompressedArray(os, state.vertexAttribCurrentValues, 4, true);
os << "buffers =" << std::endl;
for (gl::BufferBinding bufferBinding : angle::AllEnums<gl::BufferBinding>())
{
os << " [" << bufferBinding << "] = " << state.buffers[bufferBinding] << std::endl;
}
os << "indexedBuffers =" << std::endl;
for (gl::BufferBinding bufferBinding : angle::AllEnums<gl::BufferBinding>())
{
const std::vector<IndexedBufferBindingGL> &buffers = state.indexedBuffers[bufferBinding];
if (buffers.empty())
{
continue;
}
os << " [" << bufferBinding << "] =" << std::endl;
PrintCompressedArray(os, buffers, 8, true);
}
os << "textureUnitIndex = " << state.textureUnitIndex << std::endl;
os << "textures =" << std::endl;
for (gl::TextureType textureType : angle::AllEnums<gl::TextureType>())
{
os << " [" << textureType << "] =" << std::endl;
PrintCompressedArray(os, state.textures[textureType], 8, false);
}
os << "samplers =" << std::endl;
PrintCompressedArray(os, state.samplers, 4, false);
os << "images =" << std::endl;
PrintCompressedArray(os, state.images, 4, true);
os << "transformFeedback = " << state.transformFeedback << std::endl;
os << "unpackState = (" << state.unpackState << ")" << std::endl;
os << "packState = (" << state.packState << ")" << std::endl;
os << "framebuffers =" << std::endl;
os << " [GL_READ_FRAMEBUFFER] = " << state.framebuffers[angle::FramebufferBindingRead]
<< std::endl;
os << " [GL_DRAW_FRAMEBUFFER] = " << state.framebuffers[angle::FramebufferBindingDraw]
<< std::endl;
os << "renderbuffer = " << state.renderbuffer << std::endl;
os << "scissorTestEnabled = " << state.scissorTestEnabled << std::endl;
os << "scissor = (" << state.scissor << ")" << std::endl;
os << "viewport = (" << state.viewport << ")" << std::endl;
os << "near = " << state.near << std::endl;
os << "far = " << state.far << std::endl;
os << "clipOrigin = " << state.clipOrigin << std::endl;
os << "clipDepthMode = " << state.clipDepthMode << std::endl;
os << "blendColor = (" << state.blendColor << ")" << std::endl;
os << "blendState =" << std::endl;
PrintCompressedBlendState(os, state.blendState, 4);
os << "blendAdvancedCoherent = " << state.blendAdvancedCoherent << std::endl;
os << "sampleAlphaToCoverageEnabled = " << state.sampleAlphaToCoverageEnabled << std::endl;
os << "sampleCoverageEnabled = " << state.sampleCoverageEnabled << std::endl;
os << "sampleCoverageValue = " << state.sampleCoverageValue << std::endl;
os << "sampleCoverageInvert = " << state.sampleCoverageInvert << std::endl;
os << "sampleMaskEnabled = " << state.sampleMaskEnabled << std::endl;
os << "sampleMaskValues =" << std::endl;
PrintCompressedArray(os, state.sampleMaskValues, 4, false);
os << "depthTestEnabled = " << state.depthTestEnabled << std::endl;
os << "depthFunc = " << gl::FmtHex(state.depthFunc) << std::endl;
os << "depthMask = " << state.depthMask << std::endl;
os << "stencilTestEnabled = " << state.stencilTestEnabled << std::endl;
os << "stencilFrontFunc = " << gl::FmtHex(state.stencilFrontFunc) << std::endl;
os << "stencilFrontRef = " << state.stencilFrontRef << std::endl;
os << "stencilFrontValueMask = " << gl::FmtHex(state.stencilFrontValueMask) << std::endl;
os << "stencilFrontStencilFailOp = " << gl::FmtHex(state.stencilFrontStencilFailOp)
<< std::endl;
os << "stencilFrontStencilPassDepthFailOp = "
<< gl::FmtHex(state.stencilFrontStencilPassDepthFailOp) << std::endl;
os << "stencilFrontStencilPassDepthPassOp = "
<< gl::FmtHex(state.stencilFrontStencilPassDepthPassOp) << std::endl;
os << "stencilFrontWritemask = " << gl::FmtHex(state.stencilFrontWritemask) << std::endl;
os << "stencilBackFunc = " << gl::FmtHex(state.stencilBackFunc) << std::endl;
os << "stencilBackRef = " << state.stencilBackRef << std::endl;
os << "stencilBackValueMask = " << gl::FmtHex(state.stencilBackValueMask) << std::endl;
os << "stencilBackStencilFailOp = " << gl::FmtHex(state.stencilBackStencilFailOp) << std::endl;
os << "stencilBackStencilPassDepthFailOp = "
<< gl::FmtHex(state.stencilBackStencilPassDepthFailOp) << std::endl;
os << "stencilBackStencilPassDepthPassOp = "
<< gl::FmtHex(state.stencilBackStencilPassDepthPassOp) << std::endl;
os << "stencilBackWritemask = " << gl::FmtHex(state.stencilBackWritemask) << std::endl;
os << "cullFaceEnabled = " << state.cullFaceEnabled << std::endl;
os << "cullFace = " << state.cullFace << std::endl;
os << "frontFace = " << gl::FmtHex(state.frontFace) << std::endl;
os << "polygonMode = " << state.polygonMode << std::endl;
os << "polygonOffsetPointEnabled = " << state.polygonOffsetPointEnabled << std::endl;
os << "polygonOffsetLineEnabled = " << state.polygonOffsetLineEnabled << std::endl;
os << "polygonOffsetFillEnabled = " << state.polygonOffsetFillEnabled << std::endl;
os << "polygonOffsetFactor = " << state.polygonOffsetFactor << std::endl;
os << "polygonOffsetUnits = " << state.polygonOffsetUnits << std::endl;
os << "polygonOffsetClamp = " << state.polygonOffsetClamp << std::endl;
os << "depthClampEnabled = " << state.depthClampEnabled << std::endl;
os << "rasterizerDiscardEnabled = " << state.rasterizerDiscardEnabled << std::endl;
os << "lineWidth = " << state.lineWidth << std::endl;
os << "primitiveRestartFixedIndexEnabled = " << state.primitiveRestartFixedIndexEnabled
<< std::endl;
os << "primitiveRestartEnabled = " << state.primitiveRestartEnabled << std::endl;
os << "primitiveRestartIndex = " << state.primitiveRestartIndex << std::endl;
os << "clearColor = " << state.clearColor << std::endl;
os << "clearDepth = " << state.clearDepth << std::endl;
os << "clearStencil = " << state.clearStencil << std::endl;
os << "framebufferSRGBEnabled = " << state.framebufferSRGBEnabled << std::endl;
os << "ditherEnabled = " << state.ditherEnabled << std::endl;
os << "textureCubemapSeamlessEnabled = " << state.textureCubemapSeamlessEnabled << std::endl;
os << "multisamplingEnabled = " << state.multisamplingEnabled << std::endl;
os << "sampleAlphaToOneEnabled = " << state.sampleAlphaToOneEnabled << std::endl;
os << "provokingVertex = " << gl::FmtHex(state.provokingVertex) << std::endl;
os << "enabledClipDistances =" << std::endl;
PrintCompressedArray(os, state.enabledClipDistances, 4, false);
os << "logicOpEnabled = " << state.logicOpEnabled << std::endl;
os << "logicOp = " << state.logicOp << std::endl;
return os;
}
StateManagerGL::StateManagerGL(const FunctionsGL *functions,
const gl::Caps &rendererCaps,
const gl::Extensions &extensions,
const angle::FeaturesGL &features)
: mFunctions(functions),
mFeatures(features),
mCaps(functions, rendererCaps),
mState(mCaps),
mSupportsVertexArrayObjects(nativegl::SupportsVertexArrayObjects(functions)),
mCurrentTransformFeedback(nullptr),
mQueries(),
mPrevDrawContext({0}),
mIndependentBlendStates(extensions.drawBuffersIndexedAny()),
mSampleCoverageEverChanged(false),
mHasUnflushedQueries(false),
mFramebufferSRGBAvailable(extensions.sRGBWriteControlEXT),
mHasSeparateFramebufferBindings(nativegl::SupportsSeparateFramebufferBindings(functions)),
mIsMultiviewEnabled(extensions.multiviewOVR),
mMaxClipDistances(rendererCaps.maxClipDistances)
{
ASSERT(mFunctions);
ASSERT(rendererCaps.maxViews >= 1u);
mQueries.fill(nullptr);
mTemporaryPausedQueries.fill(nullptr);
// Initialize point sprite state for desktop GL
if (mFunctions->standard == STANDARD_GL_DESKTOP)
{
mFunctions->enable(GL_PROGRAM_POINT_SIZE);
// GL_POINT_SPRITE was deprecated in the core profile. Point rasterization is always
// performed
// as though POINT_SPRITE were enabled.
if ((mFunctions->profile & GL_CONTEXT_CORE_PROFILE_BIT) == 0)
{
mFunctions->enable(GL_POINT_SPRITE);
}
}
if (features.emulatePrimitiveRestartFixedIndex.enabled)
{
// There is no consistent default value for primitive restart index. Set it to UINT -1.
constexpr GLuint primitiveRestartIndex = gl::GetPrimitiveRestartIndexFromType<GLuint>();
mFunctions->primitiveRestartIndex(primitiveRestartIndex);
mState.primitiveRestartIndex = primitiveRestartIndex;
}
// It's possible we've enabled the emulated VAO feature for testing but we're on a core profile.
// Use a generated VAO as the default VAO so we can still test.
if ((features.syncAllVertexArraysToDefault.enabled ||
features.syncDefaultVertexArraysToDefault.enabled) &&
!nativegl::CanUseDefaultVertexArrayObject(mFunctions))
{
ASSERT(nativegl::SupportsVertexArrayObjects(mFunctions));
mFunctions->genVertexArrays(1, &mDefaultVAO);
mFunctions->bindVertexArray(mDefaultVAO);
mState.vao = mDefaultVAO;
}
// By default, desktop GL clamps values read from normalized
// color buffers to [0, 1], which does not match expected ES
// behavior for signed normalized color buffers.
if (mFunctions->clampColor)
{
mFunctions->clampColor(GL_CLAMP_READ_COLOR, GL_FALSE);
}
// The initial viewport and scissor state is based on the surface that this context was first
// made current with. Query it back.
GetHelper(mFunctions, GL_SCISSOR_BOX, &mState.scissor);
GetHelper(mFunctions, GL_VIEWPORT, &mState.viewport);
}
StateManagerGL::~StateManagerGL()
{
if (mPlaceholderFbo != 0)
{
deleteFramebuffer(mPlaceholderFbo);
}
if (mPlaceholderFboColorRenderbuffer != 0)
{
deleteRenderbuffer(mPlaceholderFboColorRenderbuffer);
}
if (mPlaceholderFboDepthStencilRenderbuffer != 0)
{
deleteRenderbuffer(mPlaceholderFboDepthStencilRenderbuffer);
}
if (mDefaultVAO != 0)
{
mFunctions->deleteVertexArrays(1, &mDefaultVAO);
}
}
void StateManagerGL::deleteProgram(GLuint program)
{
if (program != 0)
{
if (mState.program == program)
{
useProgram(0);
}
mFunctions->deleteProgram(program);
}
}
void StateManagerGL::deleteVertexArray(GLuint vao)
{
if (vao != 0)
{
if (mState.vao == vao)
{
bindVertexArray(0);
}
mFunctions->deleteVertexArrays(1, &vao);
mVAOStates.erase(vao);
}
}
void StateManagerGL::deleteTexture(GLuint texture)
{
if (texture != 0)
{
for (gl::TextureType type : angle::AllEnums<gl::TextureType>())
{
const auto &textureVector = mState.textures[type];
for (size_t textureUnitIndex = 0; textureUnitIndex < textureVector.size();
textureUnitIndex++)
{
if (textureVector[textureUnitIndex] == texture)
{
activeTexture(textureUnitIndex);
bindTexture(type, 0);
}
}
}
for (size_t imageUnitIndex = 0; imageUnitIndex < mState.images.size(); imageUnitIndex++)
{
if (mState.images[imageUnitIndex].texture == texture)
{
bindImageTexture(imageUnitIndex, 0, 0, false, 0, GL_READ_ONLY, GL_R32UI);
}
}
mFunctions->deleteTextures(1, &texture);
}
}
void StateManagerGL::deleteSampler(GLuint sampler)
{
if (sampler != 0)
{
for (size_t unit = 0; unit < mState.samplers.size(); unit++)
{
if (mState.samplers[unit] == sampler)
{
bindSampler(unit, 0);
}
}
mFunctions->deleteSamplers(1, &sampler);
}
}
void StateManagerGL::deleteBuffer(GLuint buffer)
{
if (buffer == 0)
{
return;
}
for (auto target : angle::AllEnums<gl::BufferBinding>())
{
if (mState.buffers[target] == buffer)
{
bindBuffer(target, 0);
}
auto &indexedTarget = mState.indexedBuffers[target];
for (size_t bindIndex = 0; bindIndex < indexedTarget.size(); ++bindIndex)
{
if (indexedTarget[bindIndex].buffer == buffer)
{
bindBufferBase(target, bindIndex, 0);
}
}
}
VertexArrayStateGL *vaoState = getCurrentVAOState();
if (vaoState)
{
if (vaoState->elementArrayBuffer == buffer)
{
vaoState->elementArrayBuffer = 0;
}
for (VertexBindingGL &binding : vaoState->bindings)
{
if (binding.buffer == buffer)
{
binding.buffer = 0;
binding.offset = 0;
}
}
}
mFunctions->deleteBuffers(1, &buffer);
}
void StateManagerGL::deleteFramebuffer(GLuint fbo)
{
if (fbo != 0)
{
bool wasBound = false;
if (mHasSeparateFramebufferBindings)
{
for (size_t binding = 0; binding < mState.framebuffers.size(); ++binding)
{
if (mState.framebuffers[binding] == fbo)
{
GLenum enumValue = angle::FramebufferBindingToEnum(
static_cast<angle::FramebufferBinding>(binding));
bindFramebuffer(enumValue, 0);
wasBound = true;
}
}
}
else
{
ASSERT(mState.framebuffers[angle::FramebufferBindingRead] ==
mState.framebuffers[angle::FramebufferBindingDraw]);
if (mState.framebuffers[angle::FramebufferBindingRead] == fbo)
{
bindFramebuffer(GL_FRAMEBUFFER, 0);
wasBound = true;
}
}
if (!wasBound && mHasUnflushedQueries &&
mFeatures.flushQueriesBeforeDeletingOrUnbindingFbo.enabled)
{
forcefullyFlush();
}
mFunctions->deleteFramebuffers(1, &fbo);
}
}
void StateManagerGL::deleteRenderbuffer(GLuint rbo)
{
if (rbo != 0)
{
if (mState.renderbuffer == rbo)
{
bindRenderbuffer(GL_RENDERBUFFER, 0);
}
mFunctions->deleteRenderbuffers(1, &rbo);
}
}
void StateManagerGL::deleteTransformFeedback(GLuint transformFeedback)
{
if (transformFeedback != 0)
{
if (mState.transformFeedback == transformFeedback)
{
bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
}
if (mCurrentTransformFeedback != nullptr &&
mCurrentTransformFeedback->getTransformFeedbackID() == transformFeedback)
{
mCurrentTransformFeedback = nullptr;
}
mFunctions->deleteTransformFeedbacks(1, &transformFeedback);
}
}
void StateManagerGL::useProgram(GLuint program)
{
if (mState.program != program)
{
forceUseProgram(program);
}
}
void StateManagerGL::forceUseProgram(GLuint program)
{
mState.program = program;
mFunctions->useProgram(program);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_PROGRAM_BINDING);
}
void StateManagerGL::bindVertexArray(GLuint vao)
{
if (mState.vao != vao)
{
ASSERT(!mFeatures.syncAllVertexArraysToDefault.enabled);
forceBindVertexArray(vao);
}
}
void StateManagerGL::forceBindVertexArray(GLuint vao)
{
VertexArrayStateGL *vaoState = getVAOState(vao);
mState.vao = vao;
mState.buffers[gl::BufferBinding::ElementArray] = vaoState ? vaoState->elementArrayBuffer : 0;
mFunctions->bindVertexArray(vao);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING);
}
void StateManagerGL::bindBuffer(gl::BufferBinding target, GLuint buffer)
{
// GL drivers differ in whether the transform feedback bind point is modified when
// glBindTransformFeedback is called. To avoid these behavior differences we shouldn't try to
// use it.
ASSERT(target != gl::BufferBinding::TransformFeedback);
if (mState.buffers[target] != buffer)
{
mState.buffers[target] = buffer;
mFunctions->bindBuffer(gl::ToGLenum(target), buffer);
setBufferBindingDirty(target);
if (target == gl::BufferBinding::ElementArray)
{
VertexArrayStateGL *vaoState = getCurrentVAOState();
if (vaoState)
{
vaoState->elementArrayBuffer = buffer;
}
}
}
}
void StateManagerGL::bindBufferBase(gl::BufferBinding target, size_t index, GLuint buffer)
{
// Transform feedback buffer bindings are tracked in TransformFeedbackGL
ASSERT(target != gl::BufferBinding::TransformFeedback);
ASSERT(index < mState.indexedBuffers[target].size());
auto &binding = mState.indexedBuffers[target][index];
if (binding.buffer != buffer || binding.offset != 0 || binding.size != 0)
{
binding.buffer = buffer;
binding.offset = 0;
binding.size = 0;
mState.buffers[target] = buffer;
mFunctions->bindBufferBase(gl::ToGLenum(target), static_cast<GLuint>(index), buffer);
setBufferBindingDirty(target);
}
}
void StateManagerGL::bindBufferRange(gl::BufferBinding target,
size_t index,
GLuint buffer,
GLintptr offset,
GLsizeiptr size)
{
// Transform feedback buffer bindings are tracked in TransformFeedbackGL
ASSERT(target != gl::BufferBinding::TransformFeedback);
auto &binding = mState.indexedBuffers[target][index];
if (binding.buffer != buffer || binding.offset != offset || binding.size != size)
{
binding.buffer = buffer;
binding.offset = offset;
binding.size = size;
mState.buffers[target] = buffer;
mFunctions->bindBufferRange(gl::ToGLenum(target), static_cast<GLuint>(index), buffer,
offset, size);
setBufferBindingDirty(target);
}
}
void StateManagerGL::activeTexture(size_t unit)
{
if (mState.textureUnitIndex != unit)
{
mState.textureUnitIndex = unit;
mFunctions->activeTexture(GL_TEXTURE0 + static_cast<GLenum>(unit));
}
}
void StateManagerGL::bindTexture(gl::TextureType type, GLuint texture)
{
if (mState.textures[type][mState.textureUnitIndex] != texture)
{
mState.textures[type][mState.textureUnitIndex] = texture;
mFunctions->bindTexture(nativegl::GetTextureBindingTarget(type), texture);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_TEXTURE_BINDINGS);
}
}
void StateManagerGL::bindTexture(gl::TextureType type, size_t unit, GLuint texture)
{
if (mState.textures[type][unit] != texture)
{
activeTexture(unit);
bindTexture(type, texture);
}
}
void StateManagerGL::bindSampler(size_t unit, GLuint sampler)
{
if (mState.samplers[unit] != sampler)
{
mState.samplers[unit] = sampler;
mFunctions->bindSampler(static_cast<GLuint>(unit), sampler);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SAMPLER_BINDINGS);
}
}
void StateManagerGL::bindImageTexture(size_t unit,
GLuint texture,
GLint level,
GLboolean layered,
GLint layer,
GLenum access,
GLenum format)
{
auto &binding = mState.images[unit];
if (binding.texture != texture || binding.level != level || binding.layered != layered ||
binding.layer != layer || binding.access != access || binding.format != format)
{
binding.texture = texture;
binding.level = level;
binding.layered = layered;
binding.layer = layer;
binding.access = access;
binding.format = format;
mFunctions->bindImageTexture(angle::base::checked_cast<GLuint>(unit), texture, level,
layered, layer, access, format);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_IMAGE_BINDINGS);
}
}
angle::Result StateManagerGL::setPixelUnpackState(const gl::Context *context,
const gl::PixelUnpackState &unpack)
{
if (mState.unpackState == unpack)
{
return angle::Result::Continue;
}
if (mState.unpackState.alignment != unpack.alignment)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_ALIGNMENT, unpack.alignment));
}
if (mState.unpackState.rowLength != unpack.rowLength)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_ROW_LENGTH, unpack.rowLength));
}
if (mState.unpackState.skipRows != unpack.skipRows)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_SKIP_ROWS, unpack.skipRows));
}
if (mState.unpackState.skipPixels != unpack.skipPixels)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_SKIP_PIXELS, unpack.skipPixels));
}
if (mState.unpackState.imageHeight != unpack.imageHeight)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_IMAGE_HEIGHT, unpack.imageHeight));
}
if (mState.unpackState.skipImages != unpack.skipImages)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_UNPACK_SKIP_IMAGES, unpack.skipImages));
}
mState.unpackState = unpack;
mLocalDirtyBits.set(gl::state::DIRTY_BIT_UNPACK_STATE);
return angle::Result::Continue;
}
angle::Result StateManagerGL::setPixelUnpackBuffer(const gl::Context *context,
const gl::Buffer *pixelBuffer)
{
GLuint bufferID = 0;
if (pixelBuffer != nullptr)
{
bufferID = GetImplAs<BufferGL>(pixelBuffer)->getBufferID();
}
bindBuffer(gl::BufferBinding::PixelUnpack, bufferID);
return angle::Result::Continue;
}
angle::Result StateManagerGL::setPixelPackState(const gl::Context *context,
const gl::PixelPackState &pack)
{
if (mState.packState == pack)
{
return angle::Result::Continue;
}
if (mState.packState.alignment != pack.alignment)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_PACK_ALIGNMENT, pack.alignment));
}
if (mState.packState.rowLength != pack.rowLength)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_PACK_ROW_LENGTH, pack.rowLength));
}
if (mState.packState.skipRows != pack.skipRows)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_PACK_SKIP_ROWS, pack.skipRows));
}
if (mState.packState.skipPixels != pack.skipPixels)
{
ANGLE_GL_TRY(context, mFunctions->pixelStorei(GL_PACK_SKIP_PIXELS, pack.skipPixels));
}
mState.packState = pack;
mLocalDirtyBits.set(gl::state::DIRTY_BIT_PACK_STATE);
return angle::Result::Continue;
}
angle::Result StateManagerGL::setPixelPackBuffer(const gl::Context *context,
const gl::Buffer *pixelBuffer)
{
GLuint bufferID = 0;
if (pixelBuffer != nullptr)
{
bufferID = GetImplAs<BufferGL>(pixelBuffer)->getBufferID();
}
bindBuffer(gl::BufferBinding::PixelPack, bufferID);
return angle::Result::Continue;
}
void StateManagerGL::bindFramebuffer(GLenum type, GLuint framebuffer)
{
bool framebufferChanged = false;
switch (type)
{
case GL_FRAMEBUFFER:
if (mState.framebuffers[angle::FramebufferBindingRead] != framebuffer ||
mState.framebuffers[angle::FramebufferBindingDraw] != framebuffer)
{
mState.framebuffers[angle::FramebufferBindingRead] = framebuffer;
mState.framebuffers[angle::FramebufferBindingDraw] = framebuffer;
mFunctions->bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING);
framebufferChanged = true;
}
break;
case GL_READ_FRAMEBUFFER:
ASSERT(mHasSeparateFramebufferBindings);
if (mState.framebuffers[angle::FramebufferBindingRead] != framebuffer)
{
mState.framebuffers[angle::FramebufferBindingRead] = framebuffer;
mFunctions->bindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
framebufferChanged = true;
}
break;
case GL_DRAW_FRAMEBUFFER:
ASSERT(mHasSeparateFramebufferBindings);
if (mState.framebuffers[angle::FramebufferBindingDraw] != framebuffer)
{
mState.framebuffers[angle::FramebufferBindingDraw] = framebuffer;
mFunctions->bindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING);
framebufferChanged = true;
}
break;
default:
UNREACHABLE();
break;
}
if (framebufferChanged)
{
if (mFeatures.flushOnFramebufferChange.enabled)
{
mFunctions->flush();
}
if (mHasUnflushedQueries && mFeatures.flushQueriesBeforeDeletingOrUnbindingFbo.enabled)
{
forcefullyFlush();
}
}
}
void StateManagerGL::onSyncedFlushOrFinish()
{
mHasUnflushedQueries = false;
}
void StateManagerGL::forcefullyFlush()
{
if (mFunctions->fenceSync != nullptr && mFunctions->clientWaitSync != nullptr &&
mFunctions->deleteSync != nullptr)
{
GLsync sync = mFunctions->fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
if (sync != nullptr)
{
mFunctions->clientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
mFunctions->deleteSync(sync);
onSyncedFlushOrFinish();
return;
}
}
// Sync creation not supported or failed; fall back to finish()
mFunctions->finish();
onSyncedFlushOrFinish();
}
void StateManagerGL::bindRenderbuffer(GLenum type, GLuint renderbuffer)
{
ASSERT(type == GL_RENDERBUFFER);
if (mState.renderbuffer != renderbuffer)
{
mState.renderbuffer = renderbuffer;
mFunctions->bindRenderbuffer(type, renderbuffer);
}
}
void StateManagerGL::bindTransformFeedback(GLenum type, GLuint transformFeedback)
{
ASSERT(type == GL_TRANSFORM_FEEDBACK);
if (mState.transformFeedback != transformFeedback)
{
// Pause the current transform feedback if one is active.
// To handle virtualized contexts, StateManagerGL needs to be able to bind a new transform
// feedback at any time, even if there is one active.
if (mCurrentTransformFeedback != nullptr &&
mCurrentTransformFeedback->getTransformFeedbackID() != transformFeedback)
{
mCurrentTransformFeedback->syncPausedState(true);
mCurrentTransformFeedback = nullptr;
}
mState.transformFeedback = transformFeedback;
mFunctions->bindTransformFeedback(type, transformFeedback);
onTransformFeedbackStateChange();
}
}
void StateManagerGL::onTransformFeedbackStateChange()
{
mLocalDirtyBits.set(gl::state::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING);
}
void StateManagerGL::beginQuery(gl::QueryType type, QueryGL *queryObject, GLuint queryId)
{
// Make sure this is a valid query type and there is no current active query of this type
ASSERT(mQueries[type] == nullptr);
ASSERT(queryId != 0);
GLuint oldFramebufferBindingDraw = mState.framebuffers[angle::FramebufferBindingDraw];
if (mFeatures.bindCompleteFramebufferForTimerQueries.enabled &&
(mState.framebuffers[angle::FramebufferBindingDraw] == 0 ||
mFunctions->checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) &&
(type == gl::QueryType::TimeElapsed || type == gl::QueryType::Timestamp))
{
ensurePlaceholderFramebuffer();
bindFramebuffer(GL_DRAW_FRAMEBUFFER, mPlaceholderFbo);
}
mQueries[type] = queryObject;
mFunctions->beginQuery(ToGLenum(type), queryId);
mHasUnflushedQueries = true;
if (oldFramebufferBindingDraw != mPlaceholderFbo)
{
bindFramebuffer(GL_DRAW_FRAMEBUFFER, oldFramebufferBindingDraw);
}
}
void StateManagerGL::endQuery(gl::QueryType type, QueryGL *queryObject, GLuint queryId)
{
ASSERT(queryObject != nullptr);
ASSERT(mQueries[type] == queryObject);
mQueries[type] = nullptr;
mFunctions->endQuery(ToGLenum(type));
mHasUnflushedQueries = true;
}
void StateManagerGL::updateDrawIndirectBufferBinding(const gl::Context *context)
{
gl::Buffer *drawIndirectBuffer =
context->getState().getTargetBuffer(gl::BufferBinding::DrawIndirect);
if (drawIndirectBuffer != nullptr)
{
const BufferGL *bufferGL = GetImplAs<BufferGL>(drawIndirectBuffer);
bindBuffer(gl::BufferBinding::DrawIndirect, bufferGL->getBufferID());
}
}
void StateManagerGL::updateDispatchIndirectBufferBinding(const gl::Context *context)
{
gl::Buffer *dispatchIndirectBuffer =
context->getState().getTargetBuffer(gl::BufferBinding::DispatchIndirect);
if (dispatchIndirectBuffer != nullptr)
{
const BufferGL *bufferGL = GetImplAs<BufferGL>(dispatchIndirectBuffer);
bindBuffer(gl::BufferBinding::DispatchIndirect, bufferGL->getBufferID());
}
}
void StateManagerGL::pauseTransformFeedback()
{
if (mCurrentTransformFeedback != nullptr)
{
mCurrentTransformFeedback->syncPausedState(true);
onTransformFeedbackStateChange();
}
}
angle::Result StateManagerGL::pauseAllQueries(const gl::Context *context)
{
for (gl::QueryType type : angle::AllEnums<gl::QueryType>())
{
QueryGL *previousQuery = mQueries[type];
if (previousQuery != nullptr)
{
ANGLE_TRY(previousQuery->pause(context));
mTemporaryPausedQueries[type] = previousQuery;
mQueries[type] = nullptr;
}
}
return angle::Result::Continue;
}
angle::Result StateManagerGL::pauseQuery(const gl::Context *context, gl::QueryType type)
{
QueryGL *previousQuery = mQueries[type];
if (previousQuery)
{
ANGLE_TRY(previousQuery->pause(context));
mTemporaryPausedQueries[type] = previousQuery;
mQueries[type] = nullptr;
}
return angle::Result::Continue;
}
angle::Result StateManagerGL::resumeAllQueries(const gl::Context *context)
{
angle::ResultAccumulator result;
for (gl::QueryType type : angle::AllEnums<gl::QueryType>())
{
QueryGL *pausedQuery = mTemporaryPausedQueries[type];
mTemporaryPausedQueries[type] = nullptr;
if (pausedQuery != nullptr)
{
ASSERT(mQueries[type] == nullptr);
result = pausedQuery->resume(context);
}
}
return result;
}
angle::Result StateManagerGL::resumeQuery(const gl::Context *context, gl::QueryType type)
{
QueryGL *pausedQuery = mTemporaryPausedQueries[type];
mTemporaryPausedQueries[type] = nullptr;
if (pausedQuery != nullptr)
{
ANGLE_TRY(pausedQuery->resume(context));
}
return angle::Result::Continue;
}
angle::Result StateManagerGL::onMakeCurrent(const gl::Context *context)
{
const gl::State &glState = context->getState();
#if defined(ANGLE_ENABLE_ASSERTS)
// Temporarily pausing queries during context switch is not supported
for (QueryGL *pausedQuery : mTemporaryPausedQueries)
{
ASSERT(pausedQuery == nullptr);
}
#endif
// If the context has changed, pause the previous context's queries
auto contextID = context->getState().getContextID();
if (contextID != mPrevDrawContext)
{
for (gl::QueryType type : angle::AllEnums<gl::QueryType>())
{
QueryGL *currentQuery = mQueries[type];
// Pause any old query object
if (currentQuery != nullptr)
{
ANGLE_TRY(currentQuery->pause(context));
mQueries[type] = nullptr;
}
// Check if this new context needs to resume a query
gl::Query *newQuery = glState.getActiveQuery(type);
if (newQuery != nullptr)
{
QueryGL *queryGL = GetImplAs<QueryGL>(newQuery);
ANGLE_TRY(queryGL->resume(context));
}
}
}
onTransformFeedbackStateChange();
mPrevDrawContext = contextID;
// Seamless cubemaps are required for ES3 and higher contexts. It should be the cheapest to set
// this state here since MakeCurrent is expected to be called less frequently than draw calls.
setTextureCubemapSeamlessEnabled(context->getClientVersion() >= gl::ES_3_0);
return angle::Result::Continue;
}
void StateManagerGL::updateProgramTextureBindings(const gl::Context *context)
{
const gl::State &glState = context->getState();
const gl::ProgramExecutable *executable = glState.getProgramExecutable();
// It is possible there is no active program during a path operation.
if (!executable)
{
return;
}
const gl::ActiveTexturesCache &textures = glState.getActiveTexturesCache();
const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask();
const gl::ActiveTextureTypeArray &textureTypes = executable->getActiveSamplerTypes();
for (size_t textureUnitIndex : activeTextures)
{
gl::TextureType textureType = textureTypes[textureUnitIndex];
gl::Texture *texture = textures[textureUnitIndex];
// A nullptr texture indicates incomplete.
if (texture != nullptr)
{
const TextureGL *textureGL = GetImplAs<TextureGL>(texture);
// The DIRTY_BIT_BOUND_AS_ATTACHMENT may get inserted when texture is attached to
// FBO and if texture is already bound, Texture::syncState will not get called and dirty
// bit not gets cleared. But this bit is not used by GL backend at all, so it is
// harmless even though we expect texture is clean when reaching here. The bit will
// still get cleared next time syncState been called.
ASSERT(!texture->hasAnyDirtyBitExcludingBoundAsAttachmentBit());
ASSERT(!textureGL->hasAnyDirtyBit());
activeTexture(textureUnitIndex);
bindTexture(textureType, textureGL->getTextureID());
}
else
{
activeTexture(textureUnitIndex);
bindTexture(textureType, 0);
}
}
}
void StateManagerGL::updateProgramStorageBufferBindings(const gl::Context *context)
{
const gl::State &glState = context->getState();
const gl::ProgramExecutable *executable = glState.getProgramExecutable();
for (size_t blockIndex = 0; blockIndex < executable->getShaderStorageBlocks().size();
blockIndex++)
{
GLuint binding = executable->getShaderStorageBlockBinding(static_cast<GLuint>(blockIndex));
const auto &shaderStorageBuffer = glState.getIndexedShaderStorageBuffer(binding);
if (shaderStorageBuffer.get() != nullptr)
{
BufferGL *bufferGL = GetImplAs<BufferGL>(shaderStorageBuffer.get());
if (shaderStorageBuffer.getSize() == 0)
{
bindBufferBase(gl::BufferBinding::ShaderStorage, binding, bufferGL->getBufferID());
}
else
{
bindBufferRange(gl::BufferBinding::ShaderStorage, binding, bufferGL->getBufferID(),
shaderStorageBuffer.getOffset(), shaderStorageBuffer.getSize());
}
}
}
}
void StateManagerGL::updateProgramUniformBufferBindings(const gl::Context *context)
{
// Sync the current program executable state
const gl::State &glState = context->getState();
const gl::ProgramExecutable *executable = glState.getProgramExecutable();
ProgramExecutableGL *executableGL = GetImplAs<ProgramExecutableGL>(executable);
// If any calls to glUniformBlockBinding have been made, make them effective. Note that if PPOs
// are ever supported in this backend, this needs to look at the Program's attached to PPOs
// instead of the PPOs own executable. This is because glUniformBlockBinding operates on
// programs directly.
executableGL->syncUniformBlockBindings();
for (size_t uniformBlockIndex = 0; uniformBlockIndex < executable->getUniformBlocks().size();
uniformBlockIndex++)
{
GLuint binding = executable->getUniformBlockBinding(static_cast<GLuint>(uniformBlockIndex));
const auto &uniformBuffer = glState.getIndexedUniformBuffer(binding);
if (uniformBuffer.get() != nullptr)
{
BufferGL *bufferGL = GetImplAs<BufferGL>(uniformBuffer.get());
if (uniformBuffer.getSize() == 0)
{
bindBufferBase(gl::BufferBinding::Uniform, binding, bufferGL->getBufferID());
}
else
{
bindBufferRange(gl::BufferBinding::Uniform, binding, bufferGL->getBufferID(),
uniformBuffer.getOffset(), uniformBuffer.getSize());
}
}
}
}
void StateManagerGL::updateProgramAtomicCounterBufferBindings(const gl::Context *context)
{
const gl::State &glState = context->getState();
const gl::ProgramExecutable *executable = glState.getProgramExecutable();
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
executable->getAtomicCounterBuffers();
for (size_t index = 0; index < atomicCounterBuffers.size(); ++index)
{
const GLuint binding = executable->getAtomicCounterBufferBinding(index);
const auto &buffer = glState.getIndexedAtomicCounterBuffer(binding);
if (buffer.get() != nullptr)
{
BufferGL *bufferGL = GetImplAs<BufferGL>(buffer.get());
if (buffer.getSize() == 0)
{
bindBufferBase(gl::BufferBinding::AtomicCounter, binding, bufferGL->getBufferID());
}
else
{
bindBufferRange(gl::BufferBinding::AtomicCounter, binding, bufferGL->getBufferID(),
buffer.getOffset(), buffer.getSize());
}
}
}
}
void StateManagerGL::updateProgramImageBindings(const gl::Context *context)
{
const gl::State &glState = context->getState();
const gl::ProgramExecutable *executable = glState.getProgramExecutable();
// It is possible there is no active program during a path operation.
if (!executable)
{
return;
}
ASSERT(context->getClientVersion() >= gl::ES_3_1 ||
context->getExtensions().shaderPixelLocalStorageANGLE ||
executable->getImageBindings().empty());
for (size_t imageUnitIndex : executable->getActiveImagesMask())
{
const gl::ImageUnit &imageUnit = glState.getImageUnit(imageUnitIndex);
const TextureGL *textureGL = SafeGetImplAs<TextureGL>(imageUnit.texture.get());
if (textureGL)
{
// Do not set layer parameters for non-layered texture types to avoid driver bugs.
const bool layered = IsLayeredTextureType(textureGL->getType());
bindImageTexture(imageUnitIndex, textureGL->getTextureID(), imageUnit.level,
layered && imageUnit.layered, layered ? imageUnit.layer : 0,
imageUnit.access, imageUnit.format);
}
else
{
bindImageTexture(imageUnitIndex, 0, imageUnit.level, imageUnit.layered, imageUnit.layer,
imageUnit.access, imageUnit.format);
}
}
}
void StateManagerGL::setAttributeCurrentData(size_t index,
const gl::VertexAttribCurrentValueData &data)
{
if (mState.vertexAttribCurrentValues[index] != data)
{
mState.vertexAttribCurrentValues[index] = data;
switch (data.Type)
{
case gl::VertexAttribType::Float:
mFunctions->vertexAttrib4fv(static_cast<GLuint>(index), data.Values.FloatValues);
break;
case gl::VertexAttribType::Int:
mFunctions->vertexAttribI4iv(static_cast<GLuint>(index), data.Values.IntValues);
break;
case gl::VertexAttribType::UnsignedInt:
mFunctions->vertexAttribI4uiv(static_cast<GLuint>(index),
data.Values.UnsignedIntValues);
break;
default:
UNREACHABLE();
}
mLocalDirtyBits.set(gl::state::DIRTY_BIT_CURRENT_VALUES);
mLocalDirtyCurrentValues.set(index);
}
}
void StateManagerGL::setScissorTestEnabled(bool enabled)
{
if (mState.scissorTestEnabled != enabled)
{
mState.scissorTestEnabled = enabled;
SetGLBoolState(mFunctions, GL_SCISSOR_TEST, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SCISSOR_TEST_ENABLED);
}
}
void StateManagerGL::setScissor(const gl::Rectangle &scissor)
{
if (scissor != mState.scissor)
{
mState.scissor = scissor;
mFunctions->scissor(scissor.x, scissor.y, scissor.width, scissor.height);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SCISSOR);
}
}
void StateManagerGL::setViewport(const gl::Rectangle &viewport)
{
if (viewport != mState.viewport)
{
mState.viewport = viewport;
mFunctions->viewport(viewport.x, viewport.y, viewport.width, viewport.height);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_VIEWPORT);
}
}
void StateManagerGL::setDepthRange(float near, float far)
{
mState.near = near;
mState.far = far;
// The glDepthRangef function isn't available until OpenGL 4.1. Prefer it when it is
// available because OpenGL ES only works in floats.
if (mFunctions->depthRangef)
{
mFunctions->depthRangef(near, far);
}
else
{
ASSERT(mFunctions->depthRange);
mFunctions->depthRange(near, far);
}
mLocalDirtyBits.set(gl::state::DIRTY_BIT_DEPTH_RANGE);
}
void StateManagerGL::setClipControl(gl::ClipOrigin origin, gl::ClipDepthMode depth)
{
if (mState.clipOrigin == origin && mState.clipDepthMode == depth)
{
return;
}
mState.clipOrigin = origin;
mState.clipDepthMode = depth;
ASSERT(mFunctions->clipControl);
mFunctions->clipControl(ToGLenum(origin), ToGLenum(depth));
if (mFeatures.resyncDepthRangeOnClipControl.enabled)
{
// Change and restore depth range to trigger internal transformation
// state resync. This is needed to apply clip control on some drivers.
const float near = mState.near;
setDepthRange(near == 0.0f ? 1.0f : 0.0f, mState.far);
setDepthRange(near, mState.far);
}
mLocalDirtyBits.set(gl::state::DIRTY_BIT_CLIP_CONTROL);
}
void StateManagerGL::setClipControlWithEmulatedClipOrigin(const gl::ProgramExecutable *executable,
GLenum frontFace,
gl::ClipOrigin origin,
gl::ClipDepthMode depth)
{
ASSERT(mFeatures.emulateClipOrigin.enabled);
if (executable)
{
updateEmulatedClipOriginUniform(executable, origin);
}
static_assert((GL_CW ^ GL_CCW) == static_cast<GLenum>(gl::ClipOrigin::UpperLeft));
setFrontFace(frontFace ^ static_cast<GLenum>(origin));
setClipControl(gl::ClipOrigin::LowerLeft, depth);
}
void StateManagerGL::setBlendEnabled(bool enabled)
{
const gl::DrawBufferMask mask =
enabled ? mState.blendState.getAllEnabledMask() : gl::DrawBufferMask::Zero();
if (mState.blendState.getEnabledMask() == mask)
{
return;
}
SetGLBoolState(mFunctions, GL_BLEND, enabled);
mState.blendState.setEnabled(enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_BLEND_ENABLED);
}
void StateManagerGL::setBlendEnabledIndexed(const gl::DrawBufferMask enabledMask)
{
if (mState.blendState.getEnabledMask() == enabledMask)
{
return;
}
// Get DrawBufferMask of buffers with different blend enable state
gl::DrawBufferMask diffMask = mState.blendState.getEnabledMask() ^ enabledMask;
const size_t diffCount = diffMask.count();
// Check if enabling or disabling blending for all buffers reduces the number of subsequent
// indexed commands. Implicitly handles the case when the new blend enable state is the same for
// all buffers.
if (diffCount > 1)
{
// The number of indexed blend enable commands in case a mass disable is used.
const size_t enabledCount = enabledMask.count();
// The mask and the number of indexed blend disable commands in case a mass enable is used.
const gl::DrawBufferMask disabledMask = enabledMask ^ mState.blendState.getAllEnabledMask();
const size_t disabledCount = disabledMask.count();
if (enabledCount < diffCount && enabledCount <= disabledCount)
{
diffMask = enabledMask;
mFunctions->disable(GL_BLEND);
}
else if (disabledCount < diffCount && disabledCount <= enabledCount)
{
diffMask = disabledMask;
mFunctions->enable(GL_BLEND);
}
}
for (size_t drawBufferIndex : diffMask)
{
SetGLIndexedBoolState(mFunctions, GL_BLEND, static_cast<GLuint>(drawBufferIndex),
enabledMask.test(drawBufferIndex));
}
mState.blendState.setEnabledMask(enabledMask);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_BLEND_ENABLED);
}
void StateManagerGL::setBlendColor(const gl::ColorF &blendColor)
{
if (mState.blendColor != blendColor)
{
mState.blendColor = blendColor;
mFunctions->blendColor(blendColor.red, blendColor.green, blendColor.blue, blendColor.alpha);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_BLEND_COLOR);
}
}
void StateManagerGL::setBlendAdvancedCoherent(bool enabled)
{
if (mState.blendAdvancedCoherent != enabled)
{
mState.blendAdvancedCoherent = enabled;
SetGLBoolState(mFunctions, GL_BLEND_ADVANCED_COHERENT_KHR, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_BLEND_ADVANCED_COHERENT);
}
}
void StateManagerGL::setBlendFuncs(const gl::BlendStateExt &blendState)
{
if (mState.blendState.getSrcColorBits() == blendState.getSrcColorBits() &&
mState.blendState.getDstColorBits() == blendState.getDstColorBits() &&
mState.blendState.getSrcAlphaBits() == blendState.getSrcAlphaBits() &&
mState.blendState.getDstAlphaBits() == blendState.getDstAlphaBits())
{
return;
}
if (!mIndependentBlendStates)
{
mFunctions->blendFuncSeparate(
ToGLenum(blendState.getSrcColorIndexed(0)), ToGLenum(blendState.getDstColorIndexed(0)),
ToGLenum(blendState.getSrcAlphaIndexed(0)), ToGLenum(blendState.getDstAlphaIndexed(0)));
}
else
{
// Get DrawBufferMask of buffers with different blend factors
gl::DrawBufferMask diffMask = mState.blendState.compareFactors(blendState);
size_t diffCount = diffMask.count();
// Check if setting all buffers to the same value reduces the number of subsequent indexed
// commands. Implicitly handles the case when the new blend function state is the same for
// all buffers.
if (diffCount > 1)
{
bool found = false;
gl::BlendStateExt::FactorStorage::Type commonSrcColor = 0;
gl::BlendStateExt::FactorStorage::Type commonDstColor = 0;
gl::BlendStateExt::FactorStorage::Type commonSrcAlpha = 0;
gl::BlendStateExt::FactorStorage::Type commonDstAlpha = 0;
for (size_t i = 0; i < mState.blendState.getDrawBufferCount() - 1; i++)
{
const gl::BlendStateExt::FactorStorage::Type tempCommonSrcColor =
blendState.expandSrcColorIndexed(i);
const gl::BlendStateExt::FactorStorage::Type tempCommonDstColor =
blendState.expandDstColorIndexed(i);
const gl::BlendStateExt::FactorStorage::Type tempCommonSrcAlpha =
blendState.expandSrcAlphaIndexed(i);
const gl::BlendStateExt::FactorStorage::Type tempCommonDstAlpha =
blendState.expandDstAlphaIndexed(i);
const gl::DrawBufferMask tempDiffMask = blendState.compareFactors(
tempCommonSrcColor, tempCommonDstColor, tempCommonSrcAlpha, tempCommonDstAlpha);
const size_t tempDiffCount = tempDiffMask.count();
if (tempDiffCount < diffCount)
{
found = true;
diffMask = tempDiffMask;
diffCount = tempDiffCount;
commonSrcColor = tempCommonSrcColor;
commonDstColor = tempCommonDstColor;
commonSrcAlpha = tempCommonSrcAlpha;
commonDstAlpha = tempCommonDstAlpha;
if (tempDiffCount == 0)
{
break; // the blend factors are the same for all buffers
}
}
}
if (found)
{
mFunctions->blendFuncSeparate(
ToGLenum(gl::BlendStateExt::FactorStorage::GetValueIndexed(0, commonSrcColor)),
ToGLenum(gl::BlendStateExt::FactorStorage::GetValueIndexed(0, commonDstColor)),
ToGLenum(gl::BlendStateExt::FactorStorage::GetValueIndexed(0, commonSrcAlpha)),
ToGLenum(gl::BlendStateExt::FactorStorage::GetValueIndexed(0, commonDstAlpha)));
}
}
for (size_t drawBufferIndex : diffMask)
{
mFunctions->blendFuncSeparatei(
static_cast<GLuint>(drawBufferIndex),
ToGLenum(blendState.getSrcColorIndexed(drawBufferIndex)),
ToGLenum(blendState.getDstColorIndexed(drawBufferIndex)),
ToGLenum(blendState.getSrcAlphaIndexed(drawBufferIndex)),
ToGLenum(blendState.getDstAlphaIndexed(drawBufferIndex)));
}
}
mState.blendState.setFactorBits(blendState.getSrcColorBits(), blendState.getDstColorBits(),
blendState.getSrcAlphaBits(), blendState.getDstAlphaBits(),
blendState.getUsesExtendedBlendFactorMask());
mLocalDirtyBits.set(gl::state::DIRTY_BIT_BLEND_FUNCS);
}
void StateManagerGL::setBlendEquations(const gl::BlendStateExt &blendState)
{
if (mState.blendState.getEquationColorBits() == blendState.getEquationColorBits() &&
mState.blendState.getEquationAlphaBits() == blendState.getEquationAlphaBits())
{
return;
}
if (!mIndependentBlendStates)
{
mFunctions->blendEquationSeparate(ToGLenum(blendState.getEquationColorIndexed(0)),
ToGLenum(blendState.getEquationAlphaIndexed(0)));
}
else
{
// Get DrawBufferMask of buffers with different blend equations
gl::DrawBufferMask diffMask = mState.blendState.compareEquations(blendState);
size_t diffCount = diffMask.count();
// Check if setting all buffers to the same value reduces the number of subsequent indexed
// commands. Implicitly handles the case when the new blend equation state is the same for
// all buffers.
if (diffCount > 1)
{
bool found = false;
gl::BlendStateExt::EquationStorage::Type commonEquationColor = 0;
gl::BlendStateExt::EquationStorage::Type commonEquationAlpha = 0;
for (size_t i = 0; i < mState.blendState.getDrawBufferCount() - 1; i++)
{
const gl::BlendStateExt::EquationStorage::Type tempCommonEquationColor =
blendState.expandEquationColorIndexed(i);
const gl::BlendStateExt::EquationStorage::Type tempCommonEquationAlpha =
blendState.expandEquationAlphaIndexed(i);
const gl::DrawBufferMask tempDiffMask =
blendState.compareEquations(tempCommonEquationColor, tempCommonEquationAlpha);
const size_t tempDiffCount = tempDiffMask.count();
if (tempDiffCount < diffCount)
{
found = true;
diffMask = tempDiffMask;
diffCount = tempDiffCount;
commonEquationColor = tempCommonEquationColor;
commonEquationAlpha = tempCommonEquationAlpha;
if (tempDiffCount == 0)
{
break; // the new blend equations are the same for all buffers
}
}
}
if (found)
{
if (commonEquationColor == commonEquationAlpha)
{
mFunctions->blendEquation(
ToGLenum(gl::BlendStateExt::EquationStorage::GetValueIndexed(
0, commonEquationColor)));
}
else
{
mFunctions->blendEquationSeparate(
ToGLenum(gl::BlendStateExt::EquationStorage::GetValueIndexed(
0, commonEquationColor)),
ToGLenum(gl::BlendStateExt::EquationStorage::GetValueIndexed(
0, commonEquationAlpha)));
}
}
}
for (size_t drawBufferIndex : diffMask)
{
gl::BlendEquationType equationColor =
blendState.getEquationColorIndexed(drawBufferIndex);
gl::BlendEquationType equationAlpha =
blendState.getEquationAlphaIndexed(drawBufferIndex);
if (equationColor == equationAlpha)
{
mFunctions->blendEquationi(static_cast<GLuint>(drawBufferIndex),
ToGLenum(equationColor));
}
else
{
mFunctions->blendEquationSeparatei(static_cast<GLuint>(drawBufferIndex),
ToGLenum(equationColor),
ToGLenum(equationAlpha));
}
}
}
mState.blendState.setEquationColorBits(blendState.getEquationColorBits(),
blendState.getUsesAdvancedBlendEquationMask());
mState.blendState.setEquationAlphaBits(blendState.getEquationAlphaBits());
mLocalDirtyBits.set(gl::state::DIRTY_BIT_BLEND_EQUATIONS);
}
void StateManagerGL::setColorMask(bool red, bool green, bool blue, bool alpha)
{
const gl::BlendStateExt::ColorMaskStorage::Type mask =
mState.blendState.expandColorMaskValue(red, green, blue, alpha);
if (mState.blendState.getColorMaskBits() != mask)
{
mFunctions->colorMask(red, green, blue, alpha);
mState.blendState.setColorMaskBits(mask);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_COLOR_MASK);
}
}
void StateManagerGL::setSampleAlphaToCoverageEnabled(bool enabled)
{
if (mState.sampleAlphaToCoverageEnabled != enabled)
{
mState.sampleAlphaToCoverageEnabled = enabled;
SetGLBoolState(mFunctions, GL_SAMPLE_ALPHA_TO_COVERAGE, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED);
}
}
void StateManagerGL::setSampleCoverageEnabled(bool enabled)
{
if (mState.sampleCoverageEnabled != enabled)
{
mState.sampleCoverageEnabled = enabled;
SetGLBoolState(mFunctions, GL_SAMPLE_COVERAGE, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED);
}
}
void StateManagerGL::forceSetSampleCoverage(float value, bool invert)
{
mState.sampleCoverageValue = value;
mState.sampleCoverageInvert = invert;
mSampleCoverageEverChanged = true;
mFunctions->sampleCoverage(value, invert);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SAMPLE_COVERAGE);
}
void StateManagerGL::setSampleCoverage(float value, bool invert)
{
if (mState.sampleCoverageValue != value || mState.sampleCoverageInvert != invert)
{
forceSetSampleCoverage(value, invert);
}
}
void StateManagerGL::setSampleMaskEnabled(bool enabled)
{
if (mState.sampleMaskEnabled != enabled)
{
mState.sampleMaskEnabled = enabled;
SetGLBoolState(mFunctions, GL_SAMPLE_MASK, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SAMPLE_MASK_ENABLED);
}
}
void StateManagerGL::setSampleMask(const gl::SampleMaskArray<GLbitfield> &maskValues)
{
for (GLuint maskNumber = 0; maskNumber < maskValues.size(); maskNumber++)
{
setSampleMaski(maskNumber, maskValues[maskNumber]);
}
}
void StateManagerGL::setSampleMaski(GLuint maskNumber, GLbitfield mask)
{
ASSERT(maskNumber < mState.sampleMaskValues.size());
if (mState.sampleMaskValues[maskNumber] != mask)
{
mState.sampleMaskValues[maskNumber] = mask;
mFunctions->sampleMaski(maskNumber, mask);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SAMPLE_MASK);
}
}
void StateManagerGL::setDepthTestEnabled(bool enabled)
{
if (mState.depthTestEnabled != enabled)
{
mState.depthTestEnabled = enabled;
SetGLBoolState(mFunctions, GL_DEPTH_TEST, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_DEPTH_TEST_ENABLED);
}
}
void StateManagerGL::setDepthFunc(GLenum depthFunc)
{
if (mState.depthFunc != depthFunc)
{
mState.depthFunc = depthFunc;
mFunctions->depthFunc(depthFunc);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_DEPTH_FUNC);
}
}
void StateManagerGL::setDepthMask(bool mask)
{
if (mState.depthMask != mask)
{
mState.depthMask = mask;
mFunctions->depthMask(mask);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_DEPTH_MASK);
}
}
void StateManagerGL::setStencilTestEnabled(bool enabled)
{
if (mState.stencilTestEnabled != enabled)
{
mState.stencilTestEnabled = enabled;
SetGLBoolState(mFunctions, GL_STENCIL_TEST, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_STENCIL_TEST_ENABLED);
}
}
void StateManagerGL::setStencilFrontWritemask(GLuint mask)
{
GLuint clippedMask = mask & 0xFF;
if (mState.stencilFrontWritemask != clippedMask)
{
mState.stencilFrontWritemask = clippedMask;
mFunctions->stencilMaskSeparate(GL_FRONT, clippedMask);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_STENCIL_WRITEMASK_FRONT);
}
}
void StateManagerGL::setStencilBackWritemask(GLuint mask)
{
GLuint clippedMask = mask & 0xFF;
if (mState.stencilBackWritemask != clippedMask)
{
mState.stencilBackWritemask = clippedMask;
mFunctions->stencilMaskSeparate(GL_BACK, clippedMask);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_STENCIL_WRITEMASK_BACK);
}
}
void StateManagerGL::setStencilFrontFuncs(GLenum func, GLint ref, GLuint mask)
{
GLuint clippedMask = mask & 0xFF;
if (mState.stencilFrontFunc != func || mState.stencilFrontRef != ref ||
mState.stencilFrontValueMask != clippedMask)
{
mState.stencilFrontFunc = func;
mState.stencilFrontRef = ref;
mState.stencilFrontValueMask = clippedMask;
mFunctions->stencilFuncSeparate(GL_FRONT, func, ref, clippedMask);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT);
}
}
void StateManagerGL::setStencilBackFuncs(GLenum func, GLint ref, GLuint mask)
{
GLuint clippedMask = mask & 0xFF;
if (mState.stencilBackFunc != func || mState.stencilBackRef != ref ||
mState.stencilBackValueMask != clippedMask)
{
mState.stencilBackFunc = func;
mState.stencilBackRef = ref;
mState.stencilBackValueMask = clippedMask;
mFunctions->stencilFuncSeparate(GL_BACK, func, ref, clippedMask);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK);
}
}
void StateManagerGL::setStencilFrontOps(GLenum sfail, GLenum dpfail, GLenum dppass)
{
if (mState.stencilFrontStencilFailOp != sfail ||
mState.stencilFrontStencilPassDepthFailOp != dpfail ||
mState.stencilFrontStencilPassDepthPassOp != dppass)
{
mState.stencilFrontStencilFailOp = sfail;
mState.stencilFrontStencilPassDepthFailOp = dpfail;
mState.stencilFrontStencilPassDepthPassOp = dppass;
mFunctions->stencilOpSeparate(GL_FRONT, sfail, dpfail, dppass);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_STENCIL_OPS_FRONT);
}
}
void StateManagerGL::setStencilBackOps(GLenum sfail, GLenum dpfail, GLenum dppass)
{
if (mState.stencilBackStencilFailOp != sfail ||
mState.stencilBackStencilPassDepthFailOp != dpfail ||
mState.stencilBackStencilPassDepthPassOp != dppass)
{
mState.stencilBackStencilFailOp = sfail;
mState.stencilBackStencilPassDepthFailOp = dpfail;
mState.stencilBackStencilPassDepthPassOp = dppass;
mFunctions->stencilOpSeparate(GL_BACK, sfail, dpfail, dppass);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_STENCIL_OPS_BACK);
}
}
void StateManagerGL::setCullFaceEnabled(bool enabled)
{
if (mState.cullFaceEnabled != enabled)
{
mState.cullFaceEnabled = enabled;
SetGLBoolState(mFunctions, GL_CULL_FACE, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_CULL_FACE_ENABLED);
}
}
void StateManagerGL::setCullFace(gl::CullFaceMode cullFace)
{
if (mState.cullFace != cullFace)
{
mState.cullFace = cullFace;
mFunctions->cullFace(ToGLenum(cullFace));
mLocalDirtyBits.set(gl::state::DIRTY_BIT_CULL_FACE);
}
}
void StateManagerGL::setFrontFace(GLenum frontFace)
{
if (mState.frontFace != frontFace)
{
mState.frontFace = frontFace;
mFunctions->frontFace(frontFace);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_FRONT_FACE);
}
}
void StateManagerGL::setPolygonMode(gl::PolygonMode mode)
{
if (mState.polygonMode != mode)
{
mState.polygonMode = mode;
if (mFunctions->standard == STANDARD_GL_DESKTOP)
{
mFunctions->polygonMode(GL_FRONT_AND_BACK, ToGLenum(mode));
}
else
{
ASSERT(mFunctions->polygonModeNV);
mFunctions->polygonModeNV(GL_FRONT_AND_BACK, ToGLenum(mode));
}
mLocalDirtyBits.set(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_POLYGON_MODE);
}
}
void StateManagerGL::setPolygonOffsetPointEnabled(bool enabled)
{
if (mState.polygonOffsetPointEnabled != enabled)
{
mState.polygonOffsetPointEnabled = enabled;
SetGLBoolState(mFunctions, GL_POLYGON_OFFSET_POINT_NV, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_POINT_ENABLED);
}
}
void StateManagerGL::setPolygonOffsetLineEnabled(bool enabled)
{
if (mState.polygonOffsetLineEnabled != enabled)
{
mState.polygonOffsetLineEnabled = enabled;
SetGLBoolState(mFunctions, GL_POLYGON_OFFSET_LINE_NV, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_LINE_ENABLED);
}
}
void StateManagerGL::setPolygonOffsetFillEnabled(bool enabled)
{
if (mState.polygonOffsetFillEnabled != enabled)
{
mState.polygonOffsetFillEnabled = enabled;
SetGLBoolState(mFunctions, GL_POLYGON_OFFSET_FILL, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED);
}
}
void StateManagerGL::setPolygonOffset(float factor, float units, float clamp)
{
if (mState.polygonOffsetFactor != factor || mState.polygonOffsetUnits != units ||
mState.polygonOffsetClamp != clamp)
{
mState.polygonOffsetFactor = factor;
mState.polygonOffsetUnits = units;
mState.polygonOffsetClamp = clamp;
if (clamp == 0.0f)
{
mFunctions->polygonOffset(factor, units);
}
else
{
ASSERT(mFunctions->polygonOffsetClampEXT);
mFunctions->polygonOffsetClampEXT(factor, units, clamp);
}
mLocalDirtyBits.set(gl::state::DIRTY_BIT_POLYGON_OFFSET);
}
}
void StateManagerGL::setDepthClampEnabled(bool enabled)
{
if (mState.depthClampEnabled != enabled)
{
mState.depthClampEnabled = enabled;
SetGLBoolState(mFunctions, GL_DEPTH_CLAMP_EXT, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_DEPTH_CLAMP_ENABLED);
}
}
void StateManagerGL::setRasterizerDiscardEnabled(bool enabled)
{
if (mState.rasterizerDiscardEnabled != enabled)
{
mState.rasterizerDiscardEnabled = enabled;
SetGLBoolState(mFunctions, GL_RASTERIZER_DISCARD, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED);
}
}
void StateManagerGL::setLineWidth(float width)
{
if (mState.lineWidth != width)
{
mState.lineWidth = width;
mFunctions->lineWidth(width);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_LINE_WIDTH);
}
}
angle::Result StateManagerGL::setPrimitiveRestartFixedIndexEnabled(const gl::Context *context,
bool enabled)
{
if (mState.primitiveRestartFixedIndexEnabled != enabled)
{
SetGLBoolState(mFunctions, GL_PRIMITIVE_RESTART_FIXED_INDEX, enabled);
mState.primitiveRestartFixedIndexEnabled = enabled;
mLocalDirtyBits.set(gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED);
}
return angle::Result::Continue;
}
angle::Result StateManagerGL::setPrimitiveRestartEnabled(const gl::Context *context, bool enabled)
{
if (mState.primitiveRestartEnabled != enabled)
{
ASSERT(mFeatures.emulatePrimitiveRestartFixedIndex.enabled);
SetGLBoolState(mFunctions, GL_PRIMITIVE_RESTART, enabled);
mState.primitiveRestartEnabled = enabled;
mLocalDirtyBits.set(gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED);
}
return angle::Result::Continue;
}
angle::Result StateManagerGL::setPrimitiveRestartIndex(const gl::Context *context, GLuint index)
{
if (mState.primitiveRestartIndex != index)
{
ANGLE_GL_TRY(context, mFunctions->primitiveRestartIndex(index));
mState.primitiveRestartIndex = index;
// No dirty bit for this state, it is not exposed to the frontend.
}
return angle::Result::Continue;
}
void StateManagerGL::setClearDepth(float clearDepth)
{
if (mState.clearDepth != clearDepth)
{
mState.clearDepth = clearDepth;
// The glClearDepthf function isn't available until OpenGL 4.1. Prefer it when it is
// available because OpenGL ES only works in floats.
if (mFunctions->clearDepthf)
{
mFunctions->clearDepthf(clearDepth);
}
else
{
ASSERT(mFunctions->clearDepth);
mFunctions->clearDepth(clearDepth);
}
mLocalDirtyBits.set(gl::state::DIRTY_BIT_CLEAR_DEPTH);
}
}
void StateManagerGL::setClearColor(const gl::ColorF &clearColor)
{
if (mState.clearColor != clearColor)
{
mState.clearColor = clearColor;
mFunctions->clearColor(clearColor.red, clearColor.green, clearColor.blue, clearColor.alpha);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_CLEAR_COLOR);
}
}
void StateManagerGL::setClearStencil(GLint clearStencil)
{
// Mask the clear stencil value to 1 byte before setting it.
// The Desktop GL spec says the driver will mask when calling glClearStencil while the GLES spec
// says it will only be masked when doing the clear. By masking it here, the value we track will
// always be the same as what the driver tracks.
GLint maskedClearValue = clearStencil & 0xFF;
if (mState.clearStencil != maskedClearValue)
{
mState.clearStencil = maskedClearValue;
mFunctions->clearStencil(maskedClearValue);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_CLEAR_STENCIL);
}
}
angle::Result StateManagerGL::syncState(const gl::Context *context,
const gl::state::DirtyBits &glDirtyBits,
const gl::state::DirtyBits &bitMask,
const gl::state::ExtendedDirtyBits &extendedDirtyBits,
const gl::state::ExtendedDirtyBits &extendedBitMask)
{
const gl::State &state = context->getState();
const gl::state::DirtyBits glAndLocalDirtyBits = (glDirtyBits | mLocalDirtyBits) & bitMask;
if (!glAndLocalDirtyBits.any())
{
return angle::Result::Continue;
}
// TODO(jmadill): Investigate only syncing vertex state for active attributes
for (auto iter = glAndLocalDirtyBits.begin(), endIter = glAndLocalDirtyBits.end();
iter != endIter; ++iter)
{
switch (*iter)
{
case gl::state::DIRTY_BIT_SCISSOR_TEST_ENABLED:
setScissorTestEnabled(state.isScissorTestEnabled());
break;
case gl::state::DIRTY_BIT_SCISSOR:
{
const gl::Rectangle &scissor = state.getScissor();
setScissor(scissor);
}
break;
case gl::state::DIRTY_BIT_VIEWPORT:
{
const gl::Rectangle &viewport = state.getViewport();
setViewport(viewport);
}
break;
case gl::state::DIRTY_BIT_DEPTH_RANGE:
setDepthRange(state.getNearPlane(), state.getFarPlane());
break;
case gl::state::DIRTY_BIT_BLEND_ENABLED:
if (mIndependentBlendStates)
{
setBlendEnabledIndexed(state.getBlendEnabledDrawBufferMask());
}
else
{
setBlendEnabled(state.isBlendEnabled());
}
break;
case gl::state::DIRTY_BIT_BLEND_COLOR:
setBlendColor(state.getBlendColor());
break;
case gl::state::DIRTY_BIT_BLEND_FUNCS:
{
setBlendFuncs(state.getBlendStateExt());
break;
}
case gl::state::DIRTY_BIT_BLEND_EQUATIONS:
{
setBlendEquations(state.getBlendStateExt());
break;
}
case gl::state::DIRTY_BIT_COLOR_MASK:
{
const gl::Framebuffer *framebuffer = state.getDrawFramebuffer();
const FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(framebuffer);
const bool disableAlphaWrite =
framebufferGL->hasEmulatedAlphaChannelTextureAttachment();
setColorMaskForFramebuffer(state.getBlendStateExt(), disableAlphaWrite);
break;
}
case gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
setSampleAlphaToCoverageEnabled(state.isSampleAlphaToCoverageEnabled());
break;
case gl::state::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED:
setSampleCoverageEnabled(state.isSampleCoverageEnabled());
break;
case gl::state::DIRTY_BIT_SAMPLE_COVERAGE:
setSampleCoverage(state.getSampleCoverageValue(), state.getSampleCoverageInvert());
break;
case gl::state::DIRTY_BIT_DEPTH_TEST_ENABLED:
setDepthTestEnabled(state.isDepthTestEnabled());
break;
case gl::state::DIRTY_BIT_DEPTH_FUNC:
setDepthFunc(state.getDepthStencilState().depthFunc);
break;
case gl::state::DIRTY_BIT_DEPTH_MASK:
setDepthMask(state.getDepthStencilState().depthMask);
break;
case gl::state::DIRTY_BIT_STENCIL_TEST_ENABLED:
setStencilTestEnabled(state.isStencilTestEnabled());
break;
case gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT:
{
const auto &depthStencilState = state.getDepthStencilState();
setStencilFrontFuncs(depthStencilState.stencilFunc, state.getStencilRef(),
depthStencilState.stencilMask);
break;
}
case gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK:
{
const auto &depthStencilState = state.getDepthStencilState();
setStencilBackFuncs(depthStencilState.stencilBackFunc, state.getStencilBackRef(),
depthStencilState.stencilBackMask);
break;
}
case gl::state::DIRTY_BIT_STENCIL_OPS_FRONT:
{
const auto &depthStencilState = state.getDepthStencilState();
setStencilFrontOps(depthStencilState.stencilFail,
depthStencilState.stencilPassDepthFail,
depthStencilState.stencilPassDepthPass);
break;
}
case gl::state::DIRTY_BIT_STENCIL_OPS_BACK:
{
const auto &depthStencilState = state.getDepthStencilState();
setStencilBackOps(depthStencilState.stencilBackFail,
depthStencilState.stencilBackPassDepthFail,
depthStencilState.stencilBackPassDepthPass);
break;
}
case gl::state::DIRTY_BIT_STENCIL_WRITEMASK_FRONT:
setStencilFrontWritemask(state.getDepthStencilState().stencilWritemask);
break;
case gl::state::DIRTY_BIT_STENCIL_WRITEMASK_BACK:
setStencilBackWritemask(state.getDepthStencilState().stencilBackWritemask);
break;
case gl::state::DIRTY_BIT_CULL_FACE_ENABLED:
setCullFaceEnabled(state.isCullFaceEnabled());
break;
case gl::state::DIRTY_BIT_CULL_FACE:
setCullFace(state.getRasterizerState().cullMode);
break;
case gl::state::DIRTY_BIT_FRONT_FACE:
if (mFeatures.emulateClipOrigin.enabled)
{
static_assert((GL_CW ^ GL_CCW) ==
static_cast<GLenum>(gl::ClipOrigin::UpperLeft));
setFrontFace(state.getRasterizerState().frontFace ^
static_cast<GLenum>(state.getClipOrigin()));
break;
}
setFrontFace(state.getRasterizerState().frontFace);
break;
case gl::state::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED:
setPolygonOffsetFillEnabled(state.isPolygonOffsetFillEnabled());
break;
case gl::state::DIRTY_BIT_POLYGON_OFFSET:
{
const auto &rasterizerState = state.getRasterizerState();
setPolygonOffset(rasterizerState.polygonOffsetFactor,
rasterizerState.polygonOffsetUnits,
rasterizerState.polygonOffsetClamp);
break;
}
case gl::state::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED:
setRasterizerDiscardEnabled(state.isRasterizerDiscardEnabled());
break;
case gl::state::DIRTY_BIT_LINE_WIDTH:
setLineWidth(state.getLineWidth());
break;
case gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED:
if (mFeatures.emulatePrimitiveRestartFixedIndex.enabled)
{
ANGLE_TRY(
setPrimitiveRestartEnabled(context, state.isPrimitiveRestartEnabled()));
}
else
{
ANGLE_TRY(setPrimitiveRestartFixedIndexEnabled(
context, state.isPrimitiveRestartEnabled()));
}
break;
case gl::state::DIRTY_BIT_CLEAR_COLOR:
setClearColor(state.getColorClearValue());
break;
case gl::state::DIRTY_BIT_CLEAR_DEPTH:
setClearDepth(state.getDepthClearValue());
break;
case gl::state::DIRTY_BIT_CLEAR_STENCIL:
setClearStencil(state.getStencilClearValue());
break;
case gl::state::DIRTY_BIT_UNPACK_STATE:
ANGLE_TRY(setPixelUnpackState(context, state.getUnpackState()));
break;
case gl::state::DIRTY_BIT_UNPACK_BUFFER_BINDING:
ANGLE_TRY(setPixelUnpackBuffer(
context, state.getTargetBuffer(gl::BufferBinding::PixelUnpack)));
break;
case gl::state::DIRTY_BIT_PACK_STATE:
ANGLE_TRY(setPixelPackState(context, state.getPackState()));
break;
case gl::state::DIRTY_BIT_PACK_BUFFER_BINDING:
ANGLE_TRY(setPixelPackBuffer(context,
state.getTargetBuffer(gl::BufferBinding::PixelPack)));
break;
case gl::state::DIRTY_BIT_DITHER_ENABLED:
setDitherEnabled(state.isDitherEnabled());
break;
case gl::state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING:
{
gl::Framebuffer *framebuffer = state.getReadFramebuffer();
// Necessary for an Intel TexImage workaround.
if (!framebuffer)
{
continue;
}
FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(framebuffer);
bindFramebuffer(
mHasSeparateFramebufferBindings ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER,
framebufferGL->getFramebufferID());
GetImplAs<ContextGL>(context)->tickGC();
break;
}
case gl::state::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
{
gl::Framebuffer *framebuffer = state.getDrawFramebuffer();
// Necessary for an Intel TexImage workaround.
if (!framebuffer)
{
continue;
}
FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(framebuffer);
bindFramebuffer(
mHasSeparateFramebufferBindings ? GL_DRAW_FRAMEBUFFER : GL_FRAMEBUFFER,
framebufferGL->getFramebufferID());
GetImplAs<ContextGL>(context)->tickGC();
if (mFeatures.resetSampleCoverageOnFBOChange.enabled && mSampleCoverageEverChanged)
{
forceSetSampleCoverage(mState.sampleCoverageValue, mState.sampleCoverageInvert);
}
const gl::ProgramExecutable *executable = state.getProgramExecutable();
if (executable)
{
updateMultiviewBaseViewLayerIndexUniform(executable, framebufferGL->getState());
}
// Changing the draw framebuffer binding sometimes requires resetting srgb blending.
iter.setLaterBit(gl::state::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE);
// If the framebuffer is emulating RGB on top of RGBA, the color mask has to be
// updated
iter.setLaterBit(gl::state::DIRTY_BIT_COLOR_MASK);
break;
}
case gl::state::DIRTY_BIT_RENDERBUFFER_BINDING:
// TODO(jmadill): implement this
break;
case gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING:
{
VertexArrayGL *vaoGL = GetImplAs<VertexArrayGL>(state.getVertexArray());
bindVertexArray(vaoGL->getVertexArrayID());
ANGLE_TRY(propagateProgramToVAO(context, state.getProgramExecutable(),
GetImplAs<VertexArrayGL>(state.getVertexArray())));
if (vaoGL->syncsToSharedState())
{
// Re-sync the vertex array because all frontend VAOs share the same backend
// state. Only sync bits that can be set in ES2.0 or 3.0
gl::VertexArray::DirtyBits dirtyBits;
gl::VertexArray::DirtyAttribBitsArray dirtyAttribBits;
gl::VertexArray::DirtyBindingBitsArray dirtBindingBits;
dirtyBits.set(gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
for (GLint attrib = 0; attrib < context->getCaps().maxVertexAttributes;
attrib++)
{
dirtyBits.set(gl::VertexArray::DIRTY_BIT_ATTRIB_0 + attrib);
dirtyAttribBits[attrib].set(gl::VertexArray::DIRTY_ATTRIB_ENABLED);
dirtyAttribBits[attrib].set(gl::VertexArray::DIRTY_ATTRIB_POINTER);
dirtyAttribBits[attrib].set(gl::VertexArray::DIRTY_ATTRIB_POINTER_BUFFER);
}
for (GLint binding = 0; binding < context->getCaps().maxVertexAttribBindings;
binding++)
{
dirtyBits.set(gl::VertexArray::DIRTY_BIT_BINDING_0 + binding);
dirtBindingBits[binding].set(gl::VertexArray::DIRTY_BINDING_DIVISOR);
}
ANGLE_TRY(
vaoGL->syncState(context, dirtyBits, &dirtyAttribBits, &dirtBindingBits));
}
break;
}
case gl::state::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING:
updateDrawIndirectBufferBinding(context);
break;
case gl::state::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING:
updateDispatchIndirectBufferBinding(context);
break;
case gl::state::DIRTY_BIT_PROGRAM_BINDING:
syncProgramState(context);
break;
case gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE:
{
const gl::ProgramExecutable *executable = state.getProgramExecutable();
if (executable)
{
iter.setLaterBit(gl::state::DIRTY_BIT_TEXTURE_BINDINGS);
if (executable->getActiveImagesMask().any())
{
iter.setLaterBit(gl::state::DIRTY_BIT_IMAGE_BINDINGS);
}
if (executable->getShaderStorageBlocks().size() > 0)
{
iter.setLaterBit(gl::state::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING);
}
if (executable->getUniformBlocks().size() > 0)
{
iter.setLaterBit(gl::state::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS);
}
if (executable->getAtomicCounterBuffers().size() > 0)
{
iter.setLaterBit(gl::state::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING);
}
if (mIsMultiviewEnabled && executable->usesMultiview())
{
updateMultiviewBaseViewLayerIndexUniform(
executable,
state.getDrawFramebuffer()->getImplementation()->getState());
}
// If the current executable does not use clip distances, the related API
// state has to be disabled to avoid runtime failures on certain drivers.
// On other drivers, that state is always emulated via a special uniform,
// which needs to be updated when switching programs.
if (mMaxClipDistances > 0)
{
iter.setLaterBit(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_CLIP_DISTANCES);
}
if (mFeatures.emulateClipOrigin.enabled)
{
updateEmulatedClipOriginUniform(executable, state.getClipOrigin());
}
}
if (!executable || !executable->hasLinkedShaderStage(gl::ShaderType::Compute))
{
ANGLE_TRY(propagateProgramToVAO(
context, executable, GetImplAs<VertexArrayGL>(state.getVertexArray())));
}
break;
}
case gl::state::DIRTY_BIT_TEXTURE_BINDINGS:
updateProgramTextureBindings(context);
break;
case gl::state::DIRTY_BIT_SAMPLER_BINDINGS:
syncSamplersState(context);
break;
case gl::state::DIRTY_BIT_IMAGE_BINDINGS:
updateProgramImageBindings(context);
break;
case gl::state::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING:
syncTransformFeedbackState(context);
break;
case gl::state::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING:
updateProgramStorageBufferBindings(context);
break;
case gl::state::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS:
updateProgramUniformBufferBindings(context);
break;
case gl::state::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING:
updateProgramAtomicCounterBufferBindings(context);
break;
case gl::state::DIRTY_BIT_MULTISAMPLING:
setMultisamplingStateEnabled(state.isMultisamplingEnabled());
break;
case gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE:
setSampleAlphaToOneStateEnabled(state.isSampleAlphaToOneEnabled());
break;
case gl::state::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE:
setFramebufferSRGBEnabledForFramebuffer(
context, state.getFramebufferSRGB(),
GetImplAs<FramebufferGL>(state.getDrawFramebuffer()));
break;
case gl::state::DIRTY_BIT_SAMPLE_MASK_ENABLED:
setSampleMaskEnabled(state.isSampleMaskEnabled());
break;
case gl::state::DIRTY_BIT_SAMPLE_MASK:
{
for (GLuint maskNumber = 0; maskNumber < state.getMaxSampleMaskWords();
++maskNumber)
{
setSampleMaski(maskNumber, state.getSampleMaskWord(maskNumber));
}
break;
}
case gl::state::DIRTY_BIT_CURRENT_VALUES:
{
gl::AttributesMask combinedMask =
(state.getAndResetDirtyCurrentValues() | mLocalDirtyCurrentValues);
for (auto attribIndex : combinedMask)
{
setAttributeCurrentData(attribIndex,
state.getVertexAttribCurrentValue(attribIndex));
}
mLocalDirtyCurrentValues.reset();
break;
}
case gl::state::DIRTY_BIT_PROVOKING_VERTEX:
setProvokingVertex(ToGLenum(state.getProvokingVertex()));
break;
case gl::state::DIRTY_BIT_CLIP_CONTROL:
if (mFeatures.emulateClipOrigin.enabled)
{
setClipControlWithEmulatedClipOrigin(
state.getProgramExecutable(), state.getRasterizerState().frontFace,
state.getClipOrigin(), state.getClipDepthMode());
break;
}
setClipControl(state.getClipOrigin(), state.getClipDepthMode());
break;
case gl::state::DIRTY_BIT_EXTENDED:
{
const gl::state::ExtendedDirtyBits glAndLocalExtendedDirtyBits =
(extendedDirtyBits | mLocalExtendedDirtyBits) & extendedBitMask;
for (size_t extendedDirtyBit : glAndLocalExtendedDirtyBits)
{
switch (extendedDirtyBit)
{
case gl::state::EXTENDED_DIRTY_BIT_CLIP_DISTANCES:
{
const gl::ProgramExecutable *executable = state.getProgramExecutable();
if (executable && executable->hasClipDistance())
{
setClipDistancesEnable(state.getEnabledClipDistances());
if (mFeatures.emulateClipDistanceState.enabled)
{
updateEmulatedClipDistanceState(
executable, state.getEnabledClipDistances());
}
}
else
{
setClipDistancesEnable(gl::ClipDistanceEnableBits());
}
break;
}
case gl::state::EXTENDED_DIRTY_BIT_DEPTH_CLAMP_ENABLED:
setDepthClampEnabled(state.isDepthClampEnabled());
break;
case gl::state::EXTENDED_DIRTY_BIT_LOGIC_OP_ENABLED:
setLogicOpEnabled(state.isLogicOpEnabled());
break;
case gl::state::EXTENDED_DIRTY_BIT_LOGIC_OP:
setLogicOp(state.getLogicOp());
break;
case gl::state::EXTENDED_DIRTY_BIT_MIPMAP_GENERATION_HINT:
break;
case gl::state::EXTENDED_DIRTY_BIT_POLYGON_MODE:
setPolygonMode(state.getPolygonMode());
break;
case gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_POINT_ENABLED:
setPolygonOffsetPointEnabled(state.isPolygonOffsetPointEnabled());
break;
case gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_LINE_ENABLED:
setPolygonOffsetLineEnabled(state.isPolygonOffsetLineEnabled());
break;
case gl::state::EXTENDED_DIRTY_BIT_SHADER_DERIVATIVE_HINT:
// These hints aren't forwarded to GL yet.
break;
case gl::state::EXTENDED_DIRTY_BIT_SHADING_RATE_QCOM:
case gl::state::EXTENDED_DIRTY_BIT_SHADING_RATE_EXT:
// Unimplemented extensions.
break;
case gl::state::EXTENDED_DIRTY_BIT_BLEND_ADVANCED_COHERENT:
setBlendAdvancedCoherent(state.isBlendAdvancedCoherentEnabled());
break;
case gl::state::EXTENDED_DIRTY_BIT_FETCH_PER_SAMPLE_ENABLED:
break;
default:
UNREACHABLE();
break;
}
mLocalExtendedDirtyBits &= ~extendedBitMask;
}
break;
}
case gl::state::DIRTY_BIT_SAMPLE_SHADING:
// Nothing to do until OES_sample_shading is implemented.
break;
case gl::state::DIRTY_BIT_PATCH_VERTICES:
// Nothing to do until EXT_tessellation_shader is implemented.
break;
default:
UNREACHABLE();
break;
}
}
mLocalDirtyBits &= ~bitMask;
return angle::Result::Continue;
}
void StateManagerGL::setFramebufferSRGBEnabled(const gl::Context *context, bool enabled)
{
if (!mFramebufferSRGBAvailable)
{
return;
}
if (mState.framebufferSRGBEnabled != enabled)
{
mState.framebufferSRGBEnabled = enabled;
SetGLBoolState(mFunctions, GL_FRAMEBUFFER_SRGB, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE);
}
}
void StateManagerGL::setFramebufferSRGBEnabledForFramebuffer(const gl::Context *context,
bool enabled,
const FramebufferGL *framebuffer)
{
if (framebuffer->isDefault())
{
// Obey the framebuffer sRGB state for blending on all framebuffers except the default
// framebuffer.
// When SRGB blending is enabled, only SRGB capable formats will use it but the default
// framebuffer will always use it if it is enabled.
// TODO(geofflang): Update this when the framebuffer binding dirty changes, when it exists.
setFramebufferSRGBEnabled(context, false);
}
else
{
setFramebufferSRGBEnabled(context, enabled);
}
}
void StateManagerGL::setColorMaskForFramebuffer(const gl::BlendStateExt &blendState,
const bool disableAlpha)
{
bool r, g, b, a;
// Given that disableAlpha can be true only on macOS backbuffers and color mask is re-synced on
// bound draw framebuffer change, switch all draw buffers color masks to avoid special case
// later.
if (!mIndependentBlendStates || disableAlpha)
{
blendState.getColorMaskIndexed(0, &r, &g, &b, &a);
setColorMask(r, g, b, disableAlpha ? false : a);
return;
}
// Check if the current mask already matches the new state
if (mState.blendState.getColorMaskBits() == blendState.getColorMaskBits())
{
return;
}
// Get DrawBufferMask of buffers with different color masks
gl::DrawBufferMask diffMask = mState.blendState.compareColorMask(blendState.getColorMaskBits());
size_t diffCount = diffMask.count();
// Check if setting all buffers to the same value reduces the number of subsequent indexed
// commands. Implicitly handles the case when the new mask is the same for all buffers.
// For instance, let's say that previously synced mask is ccccff00 and the new state is
// ffeeeeee. Instead of calling colorMaski 8 times, ANGLE can set all buffers to `e` and then
// use colorMaski only twice. On the other hand, if the new state is cceeee00, a non-indexed
// call will increase the total number of GL commands.
if (diffCount > 1)
{
bool found = false;
gl::BlendStateExt::ColorMaskStorage::Type commonColorMask = 0;
for (size_t i = 0; i < mState.blendState.getDrawBufferCount() - 1; i++)
{
const gl::BlendStateExt::ColorMaskStorage::Type tempCommonColorMask =
blendState.expandColorMaskIndexed(i);
const gl::DrawBufferMask tempDiffMask =
blendState.compareColorMask(tempCommonColorMask);
const size_t tempDiffCount = tempDiffMask.count();
if (tempDiffCount < diffCount)
{
found = true;
diffMask = tempDiffMask;
diffCount = tempDiffCount;
commonColorMask = tempCommonColorMask;
if (tempDiffCount == 0)
{
break; // the new mask is the same for all buffers
}
}
}
if (found)
{
gl::BlendStateExt::UnpackColorMask(commonColorMask, &r, &g, &b, &a);
mFunctions->colorMask(r, g, b, a);
}
}
for (size_t drawBufferIndex : diffMask)
{
blendState.getColorMaskIndexed(drawBufferIndex, &r, &g, &b, &a);
mFunctions->colorMaski(static_cast<GLuint>(drawBufferIndex), r, g, b, a);
}
mState.blendState.setColorMaskBits(blendState.getColorMaskBits());
mLocalDirtyBits.set(gl::state::DIRTY_BIT_COLOR_MASK);
}
void StateManagerGL::setDitherEnabled(bool enabled)
{
if (mState.ditherEnabled != enabled)
{
mState.ditherEnabled = enabled;
SetGLBoolState(mFunctions, GL_DITHER, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_DITHER_ENABLED);
}
}
void StateManagerGL::setMultisamplingStateEnabled(bool enabled)
{
if (mState.multisamplingEnabled != enabled)
{
mState.multisamplingEnabled = enabled;
SetGLBoolState(mFunctions, GL_MULTISAMPLE, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_MULTISAMPLING);
}
}
void StateManagerGL::setSampleAlphaToOneStateEnabled(bool enabled)
{
if (mState.sampleAlphaToOneEnabled != enabled)
{
mState.sampleAlphaToOneEnabled = enabled;
SetGLBoolState(mFunctions, GL_SAMPLE_ALPHA_TO_ONE, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE);
}
}
void StateManagerGL::setProvokingVertex(GLenum mode)
{
if (mode != mState.provokingVertex)
{
mFunctions->provokingVertex(mode);
mState.provokingVertex = mode;
mLocalDirtyBits.set(gl::state::DIRTY_BIT_PROVOKING_VERTEX);
}
}
void StateManagerGL::setClipDistancesEnable(const gl::ClipDistanceEnableBits &enables)
{
if (enables == mState.enabledClipDistances)
{
return;
}
ASSERT(mMaxClipDistances <= gl::IMPLEMENTATION_MAX_CLIP_DISTANCES);
gl::ClipDistanceEnableBits diff = enables ^ mState.enabledClipDistances;
for (size_t i : diff)
{
SetGLBoolState(mFunctions, GL_CLIP_DISTANCE0_EXT + static_cast<uint32_t>(i),
enables.test(i));
}
mState.enabledClipDistances = enables;
mLocalDirtyBits.set(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_CLIP_DISTANCES);
}
void StateManagerGL::setLogicOpEnabled(bool enabled)
{
if (enabled == mState.logicOpEnabled)
{
return;
}
mState.logicOpEnabled = enabled;
SetGLBoolState(mFunctions, GL_COLOR_LOGIC_OP, enabled);
mLocalDirtyBits.set(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_LOGIC_OP_ENABLED);
}
void StateManagerGL::setLogicOp(gl::LogicalOperation opcode)
{
if (opcode == mState.logicOp)
{
return;
}
mState.logicOp = opcode;
mFunctions->logicOp(ToGLenum(opcode));
mLocalDirtyBits.set(gl::state::DIRTY_BIT_EXTENDED);
mLocalExtendedDirtyBits.set(gl::state::EXTENDED_DIRTY_BIT_LOGIC_OP_ENABLED);
}
void StateManagerGL::setTextureCubemapSeamlessEnabled(bool enabled)
{
if (!nativegl::SupportsSettingCubemapSeamless(mFunctions))
{
return;
}
if (mState.textureCubemapSeamlessEnabled != enabled)
{
mState.textureCubemapSeamlessEnabled = enabled;
SetGLBoolState(mFunctions, GL_TEXTURE_CUBE_MAP_SEAMLESS, enabled);
}
}
angle::Result StateManagerGL::propagateProgramToVAO(const gl::Context *context,
const gl::ProgramExecutable *executable,
VertexArrayGL *vao)
{
if (vao == nullptr)
{
return angle::Result::Continue;
}
// Number of views:
if (mIsMultiviewEnabled)
{
int numViews = 1;
if (executable && executable->usesMultiview())
{
numViews = executable->getNumViews();
}
ANGLE_TRY(vao->applyNumViewsToDivisor(context, numViews));
}
// Attribute enabled mask:
if (executable)
{
ANGLE_TRY(vao->applyActiveAttribLocationsMask(context,
executable->getActiveAttribLocationsMask()));
}
return angle::Result::Continue;
}
void StateManagerGL::updateMultiviewBaseViewLayerIndexUniformImpl(
const gl::ProgramExecutable *executable,
const gl::FramebufferState &drawFramebufferState) const
{
ASSERT(mIsMultiviewEnabled && executable && executable->usesMultiview());
const ProgramExecutableGL *executableGL = GetImplAs<ProgramExecutableGL>(executable);
if (drawFramebufferState.isMultiview())
{
executableGL->enableLayeredRenderingPath(drawFramebufferState.getBaseViewIndex());
}
}
void StateManagerGL::setDefaultVAOState(const VertexArrayStateGL &state)
{
if (mState.defaultVAOState == state)
{
return;
}
bindVertexArray(0);
const bool supportsBindings = nativegl::SupportsVertexAttributeBindings(mFunctions);
const bool supportsInstancing = nativegl::SupportsInstancing(mFunctions);
bindBuffer(gl::BufferBinding::ElementArray, state.elementArrayBuffer);
for (GLint i = 0; i < mCaps.maxVertexAttributes; i++)
{
VertexAttributeGL &curAttrib = mState.defaultVAOState.attributes[i];
const VertexAttributeGL &newAttrib = state.attributes[i];
if (curAttrib.enabled != newAttrib.enabled)
{
SetVertexAttribArrayEnabled(mFunctions, i, newAttrib.enabled);
curAttrib.enabled = newAttrib.enabled;
}
// If vertex bindings are supported and the binding index has been changed, set all state
// with the binding functions. Otherwise use the old glVertexAttribPointer setting
// functions.
if (supportsBindings && newAttrib.bindingIndex != curAttrib.bindingIndex)
{
mFunctions->vertexAttribBinding(i, newAttrib.bindingIndex);
curAttrib.bindingIndex = newAttrib.bindingIndex;
if (curAttrib.format != newAttrib.format ||
curAttrib.relativeOffset != newAttrib.relativeOffset)
{
mFunctions->vertexAttribFormat(i, newAttrib.format->channelCount,
gl::ToGLenum(newAttrib.format->vertexAttribType),
newAttrib.format->isNorm(),
newAttrib.relativeOffset);
curAttrib.format = newAttrib.format;
curAttrib.relativeOffset = newAttrib.relativeOffset;
}
}
else
{
VertexBindingGL &curBinding = mState.defaultVAOState.bindings[curAttrib.bindingIndex];
const VertexBindingGL &newBinding = state.bindings[newAttrib.bindingIndex];
ASSERT(newAttrib.bindingIndex == curAttrib.bindingIndex);
if (curAttrib.format != newAttrib.format || curAttrib.pointer != newAttrib.pointer ||
curBinding.buffer != newBinding.buffer || curBinding.stride != newBinding.stride)
{
bindBuffer(gl::BufferBinding::Array, newBinding.buffer);
mFunctions->vertexAttribPointer(i, newAttrib.format->channelCount,
gl::ToGLenum(newAttrib.format->vertexAttribType),
newAttrib.format->isNorm(), newBinding.stride,
newAttrib.pointer);
curAttrib.format = newAttrib.format;
curAttrib.pointer = newAttrib.pointer;
curAttrib.relativeOffset = 0;
curBinding.buffer = newBinding.buffer;
curBinding.offset =
newBinding.buffer ? reinterpret_cast<GLintptr>(newAttrib.pointer) : 0;
curBinding.stride = newBinding.stride;
}
if (curBinding.divisor != newBinding.divisor)
{
ASSERT(supportsInstancing);
mFunctions->vertexAttribDivisor(i, newBinding.divisor);
curBinding.divisor = newBinding.divisor;
}
}
}
for (GLint i = 0; i < mCaps.maxVertexAttribBindings; i++)
{
VertexBindingGL &curBinding = mState.defaultVAOState.bindings[i];
const VertexBindingGL &newBinding = state.bindings[i];
if (curBinding.buffer != newBinding.buffer || curBinding.offset != newBinding.offset ||
curBinding.stride != newBinding.stride)
{
ASSERT(supportsBindings);
mFunctions->bindVertexBuffer(i, newBinding.buffer, newBinding.offset,
newBinding.stride);
curBinding.buffer = newBinding.buffer;
curBinding.stride = newBinding.stride;
curBinding.offset = newBinding.offset;
}
if (curBinding.divisor != newBinding.divisor)
{
ASSERT(supportsBindings);
ASSERT(supportsInstancing);
mFunctions->vertexBindingDivisor(i, newBinding.divisor);
curBinding.divisor = newBinding.divisor;
}
}
#if defined(ANGLE_EANBLE_ASSERTS)
if (mState.defaultVAOState != state)
{
std::ostringstream msg;
msg << "VAO state note equal after applying!" << std::endl;
msg << "mState.defaultVAOState:" << std::endl
<< mState.defaultVAOState << std::endl
<< std::endl;
msg << "state:" << std::endl << state << std::endl;
FATAL() << msg.str();
}
#endif
mLocalDirtyBits.set(gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING);
}
angle::Result StateManagerGL::setState(const gl::Context *context, const ContextStateGL &state)
{
useProgram(state.program);
setDefaultVAOState(
state.defaultVAOState); // Set default VAO state before binding the target vao
bindVertexArray(state.vao);
for (size_t attribIndex = 0; attribIndex < state.vertexAttribCurrentValues.size();
attribIndex++)
{
setAttributeCurrentData(attribIndex, state.vertexAttribCurrentValues[attribIndex]);
}
for (gl::BufferBinding bufferBinding : angle::AllEnums<gl::BufferBinding>())
{
if (bufferBinding == gl::BufferBinding::TransformFeedback)
{
continue;
}
// Apply the indexed bindings first since they change the generic binding.
const std::vector<IndexedBufferBindingGL> &indexedBindings =
state.indexedBuffers[bufferBinding];
for (size_t i = 0; i < indexedBindings.size(); i++)
{
const IndexedBufferBindingGL &binding = indexedBindings[i];
if (binding.size == 0)
{
ASSERT(binding.offset == 0);
bindBufferBase(bufferBinding, i, binding.buffer);
}
else
{
bindBufferRange(bufferBinding, i, binding.buffer, binding.offset, binding.size);
}
}
bindBuffer(bufferBinding, state.buffers[bufferBinding]);
}
for (size_t textureUnit = 0; textureUnit < gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES;
textureUnit++)
{
for (gl::TextureType textureType : angle::AllEnums<gl::TextureType>())
{
bindTexture(textureType, textureUnit, state.textures[textureType][textureUnit]);
}
bindSampler(textureUnit, state.samplers[textureUnit]);
}
// Apply the target texture type after syncing all textures
activeTexture(state.textureUnitIndex);
for (size_t imageUnit = 0; imageUnit < state.images.size(); imageUnit++)
{
const ImageUnitBindingGL &binding = state.images[imageUnit];
bindImageTexture(imageUnit, binding.texture, binding.level, binding.layered, binding.layer,
binding.access, binding.format);
}
bindTransformFeedback(GL_TRANSFORM_FEEDBACK, state.transformFeedback);
ANGLE_TRY(setPixelUnpackState(context, state.unpackState));
ANGLE_TRY(setPixelPackState(context, state.packState));
if (!mHasSeparateFramebufferBindings || state.framebuffers[angle::FramebufferBindingDraw] ==
state.framebuffers[angle::FramebufferBindingRead])
{
bindFramebuffer(GL_FRAMEBUFFER, state.framebuffers[angle::FramebufferBindingDraw]);
}
else
{
bindFramebuffer(GL_DRAW_FRAMEBUFFER, state.framebuffers[angle::FramebufferBindingDraw]);
bindFramebuffer(GL_READ_FRAMEBUFFER, state.framebuffers[angle::FramebufferBindingRead]);
}
bindRenderbuffer(GL_RENDERBUFFER, state.renderbuffer);
setScissorTestEnabled(state.scissorTestEnabled);
setScissor(state.scissor);
setViewport(state.viewport);
setDepthRange(state.near, state.far);
setClipControl(state.clipOrigin, state.clipDepthMode);
setBlendColor(state.blendColor);
setBlendEnabledIndexed(state.blendState.getEnabledMask());
setColorMaskForFramebuffer(state.blendState, false);
setBlendFuncs(state.blendState);
setBlendEquations(state.blendState);
setBlendAdvancedCoherent(state.blendAdvancedCoherent);
setSampleAlphaToCoverageEnabled(state.sampleAlphaToCoverageEnabled);
setSampleCoverageEnabled(state.sampleCoverageEnabled);
setSampleCoverage(state.sampleCoverageValue, state.sampleCoverageInvert);
setSampleMaskEnabled(state.sampleMaskEnabled);
setSampleMask(state.sampleMaskValues);
setDepthTestEnabled(state.depthTestEnabled);
setDepthFunc(state.depthFunc);
setDepthMask(state.depthMask);
setStencilTestEnabled(state.stencilTestEnabled);
setStencilFrontFuncs(state.stencilFrontFunc, state.stencilFrontRef,
state.stencilFrontValueMask);
setStencilFrontOps(state.stencilFrontStencilFailOp, state.stencilFrontStencilPassDepthFailOp,
state.stencilFrontStencilPassDepthPassOp);
setStencilFrontWritemask(state.stencilFrontWritemask);
setStencilBackFuncs(state.stencilBackFunc, state.stencilBackRef, state.stencilBackValueMask);
setStencilBackOps(state.stencilBackStencilFailOp, state.stencilBackStencilPassDepthFailOp,
state.stencilBackStencilPassDepthPassOp);
setStencilBackWritemask(state.stencilBackWritemask);
setCullFaceEnabled(state.cullFaceEnabled);
setCullFace(state.cullFace);
setFrontFace(state.frontFace);
setPolygonMode(state.polygonMode);
setPolygonOffsetPointEnabled(state.polygonOffsetPointEnabled);
setPolygonOffsetLineEnabled(state.polygonOffsetLineEnabled);
setPolygonOffsetFillEnabled(state.polygonOffsetFillEnabled);
setPolygonOffset(state.polygonOffsetFactor, state.polygonOffsetUnits, state.polygonOffsetClamp);
setDepthClampEnabled(state.depthClampEnabled);
setRasterizerDiscardEnabled(state.rasterizerDiscardEnabled);
setLineWidth(state.lineWidth);
ANGLE_TRY(
setPrimitiveRestartFixedIndexEnabled(context, state.primitiveRestartFixedIndexEnabled));
ANGLE_TRY(setPrimitiveRestartEnabled(context, state.primitiveRestartEnabled));
ANGLE_TRY(setPrimitiveRestartIndex(context, state.primitiveRestartIndex));
setClearColor(state.clearColor);
setClearDepth(state.clearDepth);
setClearStencil(state.clearStencil);
setFramebufferSRGBEnabled(context, state.framebufferSRGBEnabled);
setDitherEnabled(state.ditherEnabled);
setTextureCubemapSeamlessEnabled(state.textureCubemapSeamlessEnabled);
setMultisamplingStateEnabled(state.multisamplingEnabled);
setSampleAlphaToOneStateEnabled(state.sampleAlphaToOneEnabled);
setProvokingVertex(state.provokingVertex);
setClipDistancesEnable(state.enabledClipDistances);
setLogicOpEnabled(state.logicOpEnabled);
setLogicOp(state.logicOp);
#if defined(ANGLE_ENABLE_ASSERTS)
if (mState != state)
{
std::ostringstream msg;
msg << "StateManagerGL::mState does not match new state after setState is called."
<< std::endl;
msg << "StateManagerGL::mState:" << std::endl << mState << std::endl << std::endl;
msg << "new state:" << std::endl << state << std::endl;
FATAL() << msg.str();
}
#endif
return angle::Result::Continue;
}
void StateManagerGL::updateEmulatedClipDistanceState(const gl::ProgramExecutable *executable,
const gl::ClipDistanceEnableBits enables) const
{
ASSERT(mFeatures.emulateClipDistanceState.enabled);
ASSERT(executable && executable->hasClipDistance());
const ProgramExecutableGL *executableGL = GetImplAs<ProgramExecutableGL>(executable);
executableGL->updateEnabledClipDistances(static_cast<uint8_t>(enables.bits()));
}
void StateManagerGL::syncSamplersState(const gl::Context *context)
{
const gl::SamplerBindingVector &samplers = context->getState().getSamplers();
// This could be optimized by using a separate binding dirty bit per sampler.
for (size_t samplerIndex = 0; samplerIndex < samplers.size(); ++samplerIndex)
{
const gl::Sampler *sampler = samplers[samplerIndex].get();
if (sampler != nullptr)
{
SamplerGL *samplerGL = GetImplAs<SamplerGL>(sampler);
bindSampler(samplerIndex, samplerGL->getSamplerID());
}
else
{
bindSampler(samplerIndex, 0);
}
}
}
void StateManagerGL::syncTransformFeedbackState(const gl::Context *context)
{
// Set the current transform feedback state
gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
if (transformFeedback)
{
TransformFeedbackGL *transformFeedbackGL =
GetImplAs<TransformFeedbackGL>(transformFeedback);
bindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedbackGL->getTransformFeedbackID());
transformFeedbackGL->syncActiveState(context, transformFeedback->isActive(),
transformFeedback->getPrimitiveMode());
transformFeedbackGL->syncPausedState(transformFeedback->isPaused());
mCurrentTransformFeedback = transformFeedbackGL;
}
else
{
bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
mCurrentTransformFeedback = nullptr;
}
syncProgramState(context);
}
void StateManagerGL::syncProgramState(const gl::Context *context)
{
gl::Program *program = context->getState().getProgram();
if (program != nullptr)
{
ProgramGL *programGL = GetImplAs<ProgramGL>(program);
useProgram(programGL->getProgramID());
}
}
GLuint StateManagerGL::getDefaultVAO() const
{
return mDefaultVAO;
}
void StateManagerGL::setDefaultVAOStateDirty()
{
mLocalDirtyBits.set(gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING);
}
VertexArrayStateGL *StateManagerGL::getVAOState(GLuint vao)
{
// Special case: The default VAO state is stored in ContextStateGL
if (vao == mDefaultVAO)
{
return &mState.defaultVAOState;
}
auto iter = mVAOStates.find(vao);
return (iter != mVAOStates.end()) ? &iter->second : nullptr;
}
VertexArrayStateGL *StateManagerGL::getOrCreateVAOState(GLuint vao)
{
VertexArrayStateGL *vaoState = getVAOState(vao);
if (vaoState != nullptr)
{
return vaoState;
}
auto insertResult = mVAOStates.insert(std::pair{
vao, VertexArrayStateGL(mCaps.maxVertexAttributes, mCaps.maxVertexAttribBindings)});
ASSERT(insertResult.second);
return &insertResult.first->second;
}
VertexArrayStateGL *StateManagerGL::getCurrentVAOState()
{
return getVAOState(mState.vao);
}
void StateManagerGL::validateState()
{
ensurePlaceholderFramebuffer();
ContextStateGL queriedState(mCaps);
QueryContextStateGL(mFunctions, mPlaceholderFbo, &queriedState);
if (mState != queriedState)
{
std::ostringstream msg;
msg << "Queried state does not match tracked state!" << std::endl;
msg << "Tracked state:" << std::endl << mState << std::endl << std::endl;
msg << "Queried state:" << std::endl << queriedState << std::endl;
FATAL() << msg.str();
}
}
void StateManagerGL::setBufferBindingDirty(gl::BufferBinding binding)
{
std::optional<gl::state::DirtyBitType> bit = GetBufferBindingDirtyBit(binding);
if (bit.has_value())
{
mLocalDirtyBits.set(bit.value());
}
}
std::unique_ptr<ContextStateGL> StateManagerGL::createContextStateGL() const
{
return std::make_unique<ContextStateGL>(mCaps);
}
angle::Result StateManagerGL::syncFromNativeContext(const gl::Context *context,
ContextStateGL *outNativeContextState)
{
auto *platform = ANGLEPlatformCurrent();
double startTime = platform->currentTime(platform);
ensurePlaceholderFramebuffer();
// Query the current driver state into outNativeContextState so that it can be restored later.
QueryContextStateGL(mFunctions, mPlaceholderFbo, outNativeContextState);
// Compute the dirty bits between the currently tracked state and the queried state in
// outNativeContextState. This will ensure that all frontend states will be re-applied in the
// next syncState.
ComputeDirtyBitsBetweenStates(mState, *outNativeContextState, &mLocalDirtyBits,
&mLocalExtendedDirtyBits, &mLocalDirtyCurrentValues);
// Set the currently tracked state to the queried state so that our understanding of the driver
// state is correct.
mState = *outNativeContextState;
double delta = platform->currentTime(platform) - startTime;
int us = static_cast<int>(delta * 1000000.0);
ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.SyncFromNativeContextMicroseconds", us);
return angle::Result::Continue;
}
angle::Result StateManagerGL::restoreNativeContext(const gl::Context *context,
const ContextStateGL &nativeContextState)
{
// Simply apply all state from the external nativeContextState
ANGLE_TRY(setState(context, nativeContextState));
ASSERT(mState == nativeContextState);
return angle::Result::Continue;
}
void StateManagerGL::ensurePlaceholderFramebuffer()
{
if (mPlaceholderFbo)
{
return;
}
GLenum framebufferBinding =
mHasSeparateFramebufferBindings ? GL_DRAW_FRAMEBUFFER : GL_FRAMEBUFFER;
mFunctions->genFramebuffers(1, &mPlaceholderFbo);
mFunctions->bindFramebuffer(framebufferBinding, mPlaceholderFbo);
mFunctions->genRenderbuffers(1, &mPlaceholderFboColorRenderbuffer);
mFunctions->bindRenderbuffer(GL_RENDERBUFFER, mPlaceholderFboColorRenderbuffer);
mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 2, 2);
mFunctions->framebufferRenderbuffer(framebufferBinding, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
mPlaceholderFboColorRenderbuffer);
mFunctions->genRenderbuffers(1, &mPlaceholderFboDepthStencilRenderbuffer);
mFunctions->bindRenderbuffer(GL_RENDERBUFFER, mPlaceholderFboDepthStencilRenderbuffer);
mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 2, 2);
mFunctions->framebufferRenderbuffer(framebufferBinding, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, mPlaceholderFboDepthStencilRenderbuffer);
// This ensures renderbuffer attachment is not lazy.
mFunctions->checkFramebufferStatus(framebufferBinding);
// Reset state
mFunctions->bindFramebuffer(framebufferBinding,
mState.framebuffers[angle::FramebufferBindingDraw]);
mFunctions->bindRenderbuffer(GL_RENDERBUFFER, mState.renderbuffer);
}
} // namespace rx