[Feat] (RenderState): implement glClipControl, glPolygonOffsetClamp and glTextureBarrier instead of stubbing them

This commit is contained in:
2026-08-27 05:37:16 -04:00
parent c9c2dcb42a
commit 0e4302b399
10 changed files with 273 additions and 5 deletions
@@ -846,6 +846,27 @@ namespace MobileGL::MG_Impl::GLImpl {
memoryBarrier(barriers);
}
void TextureBarrier() {
// GL 4.5 core 8.26 / GL_ARB_texture_barrier: order every write the fixed-function
// framebuffer has already issued ahead of every subsequent texture fetch, so a shader may
// read texels of a texture that is also attached to the current framebuffer.
//
// Both backends serve this through their existing memory-barrier hook rather than a new
// entry point of their own: GL_FRAMEBUFFER_BARRIER_BIT is the source half (framebuffer
// writes) and GL_TEXTURE_FETCH_BARRIER_BIT the destination half (texture fetches), which
// is exactly the dependency ARB_texture_barrier defines - just expressed with the wider
// scope glMemoryBarrier gives it. That is a superset of the required ordering, never a
// subset, so it cannot under-synchronize.
auto memoryBarrier = MG_Backend::gBackendFunctionsTable.GL.MemoryBarrier;
if (!memoryBarrier) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Backend does not support memory barriers."));
return;
}
memoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT);
}
void MemoryBarrierByRegion(GLbitfield barriers) {
if (!ValidateMemoryBarrierBits(__func__, barriers)) return;
auto memoryBarrierByRegion = MG_Backend::gBackendFunctionsTable.GL.MemoryBarrierByRegion;
@@ -35,6 +35,7 @@ namespace MobileGL::MG_Impl::GLImpl {
void PatchParameterfv(GLenum pname, const GLfloat* values);
void MemoryBarrier(GLbitfield barriers);
void MemoryBarrierByRegion(GLbitfield barriers);
void TextureBarrier();
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
@@ -994,7 +994,7 @@ DECLARE_GL_FUNCTION_HEAD(void, BindTextures, GLuint first, GLsizei count, const
DECLARE_GL_FUNCTION_HEAD(void, BindSamplers, GLuint first, GLsizei count, const GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindSamplers, first, count, samplers)
DECLARE_GL_FUNCTION_HEAD(void, BindImageTextures, GLuint first, GLsizei count, const GLuint* textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindImageTextures, first, count, textures)
DECLARE_GL_FUNCTION_HEAD(void, BindVertexBuffers, GLuint first, GLsizei count, const GLuint* buffers, const GLintptr* offsets, const GLsizei* strides) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindVertexBuffers, first, count, buffers, offsets, strides)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClipControl, GLenum origin, GLenum depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClipControl, origin, depth)
DECLARE_GL_FUNCTION_HEAD(void, ClipControl, GLenum origin, GLenum depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClipControl, origin, depth)
DECLARE_GL_FUNCTION_HEAD(void, CreateTransformFeedbacks, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateTransformFeedbacks, n, ids)
DECLARE_GL_FUNCTION_HEAD(void, TransformFeedbackBufferBase, GLuint xfb, GLuint index, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TransformFeedbackBufferBase, xfb, index, buffer)
DECLARE_GL_FUNCTION_HEAD(void, TransformFeedbackBufferRange, GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TransformFeedbackBufferRange, xfb, index, buffer, offset, size)
@@ -1107,11 +1107,11 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnConvolutionFilter, GLenum target, GLenum
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnSeparableFilter, GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void* row, GLsizei columnBufSize, void* column, void* span) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnSeparableFilter, target, format, type, rowBufSize, row, columnBufSize, column, span)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnHistogram, GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void* values) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnHistogram, target, reset, format, type, bufSize, values)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnMinmax, GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void* values) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnMinmax, target, reset, format, type, bufSize, values)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBarrier, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBarrier, )
DECLARE_GL_FUNCTION_HEAD(void, TextureBarrier, void) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureBarrier, )
DECLARE_GL_FUNCTION_STUB_HEAD(void, SpecializeShader, GLuint shader, const GLchar* pEntryPoint, GLuint numSpecializationConstants, const GLuint* pConstantIndex, const GLuint* pConstantValue) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SpecializeShader, shader, pEntryPoint, numSpecializationConstants, pConstantIndex, pConstantValue)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawArraysIndirectCount, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawArraysIndirectCount, mode, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsIndirectCount, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsIndirectCount, mode, type, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PolygonOffsetClamp, GLfloat factor, GLfloat units, GLfloat clamp) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PolygonOffsetClamp, factor, units, clamp)
DECLARE_GL_FUNCTION_HEAD(void, PolygonOffsetClamp, GLfloat factor, GLfloat units, GLfloat clamp) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PolygonOffsetClamp, factor, units, clamp)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PrimitiveBoundingBoxARB, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) DECLARE_GL_FUNCTION_STUB_END(void, PrimitiveBoundingBoxARB, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW)
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint64, GetTextureHandleARB, GLuint texture) DECLARE_GL_FUNCTION_STUB_END(GLuint64, GetTextureHandleARB, texture)
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint64, GetTextureSamplerHandleARB, GLuint texture, GLuint sampler) DECLARE_GL_FUNCTION_STUB_END(GLuint64, GetTextureSamplerHandleARB, texture, sampler)
@@ -2049,7 +2049,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetPixelTransformParameterivEXT, GLenum targ
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetPixelTransformParameterfvEXT, GLenum target, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetPixelTransformParameterfvEXT, target, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PointParameterfEXT, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PointParameterfEXT, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PointParameterfvEXT, GLenum pname, const GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PointParameterfvEXT, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PolygonOffsetClampEXT, GLfloat factor, GLfloat units, GLfloat clamp) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PolygonOffsetClampEXT, factor, units, clamp)
DECLARE_GL_FUNCTION_HEAD(void, PolygonOffsetClampEXT, GLfloat factor, GLfloat units, GLfloat clamp) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PolygonOffsetClamp, factor, units, clamp)
DECLARE_GL_FUNCTION_HEAD(void, ProvokingVertexEXT, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProvokingVertex, mode)
DECLARE_GL_FUNCTION_STUB_HEAD(void, RasterSamplesEXT, GLuint samples, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, RasterSamplesEXT, samples, fixedsamplelocations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, SecondaryColor3bEXT, GLbyte red, GLbyte green, GLbyte blue) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SecondaryColor3bEXT, red, green, blue)
@@ -2546,7 +2546,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ShadingRateImageBarrierNV, GLboolean synchro
DECLARE_GL_FUNCTION_STUB_HEAD(void, ShadingRateImagePaletteNV, GLuint viewport, GLuint first, GLsizei count, const GLenum* rates) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ShadingRateImagePaletteNV, viewport, first, count, rates)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ShadingRateSampleOrderNV, GLenum order) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ShadingRateSampleOrderNV, order)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ShadingRateSampleOrderCustomNV, GLenum rate, GLuint samples, const GLint* locations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ShadingRateSampleOrderCustomNV, rate, samples, locations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBarrierNV, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBarrierNV, )
DECLARE_GL_FUNCTION_HEAD(void, TextureBarrierNV, void) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureBarrier, )
DECLARE_GL_FUNCTION_STUB_HEAD(void, TexImage2DMultisampleCoverageNV, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexImage2DMultisampleCoverageNV, target, coverageSamples, colorSamples, internalFormat, width, height, fixedSampleLocations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TexImage3DMultisampleCoverageNV, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexImage3DMultisampleCoverageNV, target, coverageSamples, colorSamples, internalFormat, width, height, depth, fixedSampleLocations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureImage2DMultisampleNV, GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureImage2DMultisampleNV, texture, target, samples, internalFormat, width, height, fixedSampleLocations)
@@ -340,6 +340,38 @@ namespace MobileGL::MG_Impl::GLImpl {
MG_State::pGLContext->SetPolygonOffset(static_cast<Float>(factor), static_cast<Float>(units));
}
void PolygonOffsetClamp_State(GLfloat factor, GLfloat units, GLfloat clamp) {
// GL 4.6 core 14.6.5 / GL_EXT_polygon_offset_clamp. No error cases: any three floats are
// legal, and clamp = 0 is exactly glPolygonOffset. Whether the backend can APPLY the clamp
// is a separate question (see the DirectGLES/DirectVulkan forwarding); the state is
// recorded either way, because GL_POLYGON_OFFSET_CLAMP has to read back what was written.
MG_State::pGLContext->SetPolygonOffsetClamped(static_cast<Float>(factor), static_cast<Float>(units),
static_cast<Float>(clamp));
}
void ClipControl_State(GLenum origin, GLenum depth) {
// GL 4.5 core 13.5: both arguments are strict enums, and either being wrong is
// GL_INVALID_ENUM with the state left untouched.
if (origin != GL_LOWER_LEFT && origin != GL_UPPER_LEFT) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"glClipControl origin must be GL_LOWER_LEFT or GL_UPPER_LEFT; got " +
MG_Util::ConvertGLEnumToString(origin) + "."));
return;
}
if (depth != GL_NEGATIVE_ONE_TO_ONE && depth != GL_ZERO_TO_ONE) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__,
"glClipControl depth must be GL_NEGATIVE_ONE_TO_ONE or GL_ZERO_TO_ONE; got " +
MG_Util::ConvertGLEnumToString(depth) + "."));
return;
}
MG_State::pGLContext->SetClipControl(origin, depth);
}
void PolygonMode_State(GLenum face, GLenum mode) {
// GL 3.3 core: separate front/back polygon modes were removed in 3.1, so the only legal
// face is GL_FRONT_AND_BACK. GL_FRONT / GL_BACK must be rejected (some desktop drivers
@@ -1029,6 +1061,14 @@ namespace MobileGL::MG_Impl::GLImpl {
PolygonOffset_State(factor, units);
}
void PolygonOffsetClamp(GLfloat factor, GLfloat units, GLfloat clamp) {
PolygonOffsetClamp_State(factor, units, clamp);
}
void ClipControl(GLenum origin, GLenum depth) {
ClipControl_State(origin, depth);
}
void PolygonMode(GLenum face, GLenum mode) {
PolygonMode_State(face, mode);
}
@@ -40,6 +40,8 @@ namespace MobileGL::MG_Impl::GLImpl {
void SampleCoverage(GLfloat value, GLboolean invert);
void MinSampleShading(GLfloat value);
void PolygonOffset(GLfloat factor, GLfloat units);
void PolygonOffsetClamp(GLfloat factor, GLfloat units, GLfloat clamp);
void ClipControl(GLenum origin, GLenum depth);
void PolygonMode(GLenum face, GLenum mode);
void PointSize(GLfloat size);
void PointParameterf(GLenum pname, GLfloat param);
+20
View File
@@ -848,6 +848,26 @@ namespace MobileGL::MG_State {
return m_renderState.GetPolygonOffsetUnits();
}
void GLContext::SetPolygonOffsetClamped(Float factor, Float units, Float clamp) {
m_renderState.SetPolygonOffsetClamped(factor, units, clamp);
}
Float GLContext::GetPolygonOffsetClamp() const {
return m_renderState.GetPolygonOffsetClamp();
}
void GLContext::SetClipControl(GLenum origin, GLenum depth) {
m_renderState.SetClipControl(origin, depth);
}
GLenum GLContext::GetClipOrigin() const {
return m_renderState.GetClipOrigin();
}
GLenum GLContext::GetClipDepthMode() const {
return m_renderState.GetClipDepthMode();
}
void GLContext::SetCapability(CapabilityInput cap, Bool enabled) {
m_renderState.SetCapability(cap, enabled);
}
+5
View File
@@ -218,8 +218,13 @@ namespace MobileGL {
void SetPatchDefaultInnerLevel(const FloatVec2& levels);
const FloatVec2& GetPatchDefaultInnerLevel() const;
void SetPolygonOffset(Float factor, Float units);
void SetPolygonOffsetClamped(Float factor, Float units, Float clamp);
Float GetPolygonOffsetFactor() const;
Float GetPolygonOffsetUnits() const;
Float GetPolygonOffsetClamp() const;
void SetClipControl(GLenum origin, GLenum depth);
GLenum GetClipOrigin() const;
GLenum GetClipDepthMode() const;
void SetHint(GLenum target, GLenum mode);
GLenum GetHint(GLenum target) const;
void SetPointFadeThresholdSize(Float size);
@@ -263,6 +263,37 @@ namespace MobileGL {
return m_parameters.PolygonOffsetUnits;
}
void RenderState::SetPolygonOffsetClamped(Float factor, Float units, Float clamp) {
if (m_parameters.PolygonOffsetFactor == factor && m_parameters.PolygonOffsetUnits == units &&
m_parameters.PolygonOffsetClamp == clamp)
return;
m_parameters.PolygonOffsetFactor = factor;
m_parameters.PolygonOffsetUnits = units;
m_parameters.PolygonOffsetClamp = clamp;
++m_version;
}
Float RenderState::GetPolygonOffsetClamp() const {
return m_parameters.PolygonOffsetClamp;
}
void RenderState::SetClipControl(GLenum origin, GLenum depth) {
if (m_parameters.ClipOrigin == origin && m_parameters.ClipDepthMode == depth) return;
m_parameters.ClipOrigin = origin;
m_parameters.ClipDepthMode = depth;
++m_version;
}
GLenum RenderState::GetClipOrigin() const {
return m_parameters.ClipOrigin;
}
GLenum RenderState::GetClipDepthMode() const {
return m_parameters.ClipDepthMode;
}
// -------------------- Capabilities --------------------
namespace {
// CapabilityInput lists ClipDistance0..7 contiguously (RenderState.h); the caller
@@ -249,6 +249,15 @@ namespace MobileGL {
FloatVec2 PatchDefaultInnerLevel = FloatVec2(1.0f, 1.0f);
Float PolygonOffsetFactor = 0.0f;
Float PolygonOffsetUnits = 0.0f;
// GL_POLYGON_OFFSET_CLAMP (GL 4.6 core 14.6.5 / GL_EXT_polygon_offset_clamp): the maximum
// magnitude of the offset glPolygonOffsetClamp's third argument allows. Zero - the default
// - means "no clamp", which is exactly the behaviour glPolygonOffset leaves behind.
Float PolygonOffsetClamp = 0.0f;
// glClipControl (GL 4.5 core 13.5). Defaults per table 23.7 are the pre-4.5 fixed
// behaviour: origin at the lower left, depth mapped from -1..1.
GLenum ClipOrigin = GL_LOWER_LEFT;
GLenum ClipDepthMode = GL_NEGATIVE_ONE_TO_ONE;
// Blending
Array<PerBufferBlendState, MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS> BlendStates;
@@ -391,8 +400,16 @@ namespace MobileGL {
void SetPatchDefaultInnerLevel(const FloatVec2& levels);
const FloatVec2& GetPatchDefaultInnerLevel() const;
void SetPolygonOffset(Float factor, Float units);
// glPolygonOffsetClamp. Writes the same factor/units as glPolygonOffset plus the
// clamp, because that is what the entry point does - glPolygonOffset is the
// clamp = 0 case of it (GL 4.6 core 14.6.5).
void SetPolygonOffsetClamped(Float factor, Float units, Float clamp);
Float GetPolygonOffsetFactor() const;
Float GetPolygonOffsetUnits() const;
Float GetPolygonOffsetClamp() const;
void SetClipControl(GLenum origin, GLenum depth);
GLenum GetClipOrigin() const;
GLenum GetClipDepthMode() const;
// Hints. target must be one of the 4 GL 3.3 core hint targets (validated by the caller).
void SetHint(GLenum target, GLenum mode);
GLenum GetHint(GLenum target) const;
+131
View File
@@ -885,3 +885,134 @@ TEST_F(RenderStateTest, SampleShadingEnableIsStoredAndQueryable) {
EXPECT_EQ(MG_Impl::GLImpl::IsEnabled(GL_SAMPLE_SHADING), GL_FALSE);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
}
// glClipControl and glPolygonOffsetClamp were DECLARE_GL_FUNCTION_STUB_HEAD entry points: they
// took their arguments, recorded nothing and raised no error, and the state variables they own
// (GL_CLIP_ORIGIN, GL_CLIP_DEPTH_MODE, GL_POLYGON_OFFSET_CLAMP) had no arm in any getter, so the
// very first query of a conformance case raised GL_INVALID_ENUM and killed it. These assertions
// are state-shaped on purpose - the rasterization half of clip control is a backend question, but
// the state machine has to round-trip regardless of what a backend does with it.
TEST_F(RenderStateTest, ClipControlStateRoundTripsAndDefaultsToLowerLeftNegativeOneToOne) {
DrainPendingGlErrors();
GLint origin = 0;
GLint depthMode = 0;
MG_Impl::GLImpl::GetIntegerv(GL_CLIP_ORIGIN, &origin);
MG_Impl::GLImpl::GetIntegerv(GL_CLIP_DEPTH_MODE, &depthMode);
EXPECT_EQ(origin, GL_LOWER_LEFT);
EXPECT_EQ(depthMode, GL_NEGATIVE_ONE_TO_ONE);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
MG_Impl::GLImpl::ClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE);
MG_Impl::GLImpl::GetIntegerv(GL_CLIP_ORIGIN, &origin);
MG_Impl::GLImpl::GetIntegerv(GL_CLIP_DEPTH_MODE, &depthMode);
EXPECT_EQ(origin, GL_UPPER_LEFT);
EXPECT_EQ(depthMode, GL_ZERO_TO_ONE);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
// Every getter flavour has to answer, not just the integer one - the conformance suite reads
// this state through all of them.
GLfloat asFloat = 0.0f;
MG_Impl::GLImpl::GetFloatv(GL_CLIP_ORIGIN, &asFloat);
EXPECT_EQ(static_cast<GLint>(asFloat), GL_UPPER_LEFT);
GLint64 asInt64 = 0;
MG_Impl::GLImpl::GetInteger64v(GL_CLIP_DEPTH_MODE, &asInt64);
EXPECT_EQ(static_cast<GLint>(asInt64), GL_ZERO_TO_ONE);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
MG_Impl::GLImpl::ClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
}
TEST_F(RenderStateTest, ClipControlRejectsBadEnumsAndLeavesTheStateAlone) {
DrainPendingGlErrors();
MG_Impl::GLImpl::ClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE);
DrainPendingGlErrors();
MG_Impl::GLImpl::ClipControl(GL_FRONT, GL_ZERO_TO_ONE);
ExpectSingleGlError(GL_INVALID_ENUM);
MG_Impl::GLImpl::ClipControl(GL_UPPER_LEFT, GL_FRONT);
ExpectSingleGlError(GL_INVALID_ENUM);
GLint origin = 0;
GLint depthMode = 0;
MG_Impl::GLImpl::GetIntegerv(GL_CLIP_ORIGIN, &origin);
MG_Impl::GLImpl::GetIntegerv(GL_CLIP_DEPTH_MODE, &depthMode);
EXPECT_EQ(origin, GL_UPPER_LEFT) << "a rejected glClipControl must not change the state";
EXPECT_EQ(depthMode, GL_ZERO_TO_ONE) << "a rejected glClipControl must not change the state";
MG_Impl::GLImpl::ClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
DrainPendingGlErrors();
}
TEST_F(RenderStateTest, PolygonOffsetClampStoresTheClampAndTheFactorUnitsPair) {
DrainPendingGlErrors();
GLfloat clamp = -1.0f;
MG_Impl::GLImpl::GetFloatv(GL_POLYGON_OFFSET_CLAMP, &clamp);
EXPECT_FLOAT_EQ(clamp, 0.0f) << "the default clamp is zero, i.e. no clamping";
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
MG_Impl::GLImpl::PolygonOffsetClamp(1.5f, 2.5f, 0.5f);
GLfloat factor = 0.0f;
GLfloat units = 0.0f;
MG_Impl::GLImpl::GetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
MG_Impl::GLImpl::GetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
MG_Impl::GLImpl::GetFloatv(GL_POLYGON_OFFSET_CLAMP, &clamp);
EXPECT_FLOAT_EQ(factor, 1.5f);
EXPECT_FLOAT_EQ(units, 2.5f);
EXPECT_FLOAT_EQ(clamp, 0.5f) << "the fractional clamp must survive - the integer path rounds it away";
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
// glcPolygonOffsetClampTests reads GL_POLYGON_OFFSET_CLAMP through all five getters and
// requires no error from any of them; that is what used to kill the availability case.
GLboolean asBoolean = GL_FALSE;
MG_Impl::GLImpl::GetBooleanv(GL_POLYGON_OFFSET_CLAMP, &asBoolean);
EXPECT_EQ(asBoolean, GL_TRUE);
GLint asInt = -1;
MG_Impl::GLImpl::GetIntegerv(GL_POLYGON_OFFSET_CLAMP, &asInt);
EXPECT_EQ(asInt, 1) << "0.5 rounds to nearest for the integer query";
GLint64 asInt64 = -1;
MG_Impl::GLImpl::GetInteger64v(GL_POLYGON_OFFSET_CLAMP, &asInt64);
EXPECT_EQ(asInt64, 1);
GLdouble asDouble = -1.0;
MG_Impl::GLImpl::GetDoublev(GL_POLYGON_OFFSET_CLAMP, &asDouble);
EXPECT_NEAR(asDouble, 0.5, 1e-6);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
// glPolygonOffset is the clamp = 0 case of the same state, but it must not DISTURB the clamp
// it does not take - GL 4.6 core 14.6.5 defines it as PolygonOffsetClamp(factor, units, 0)
// only in the sense that the clamp it leaves is whatever glPolygonOffset itself sets, which
// for MobileGL is "unchanged". Assert the factor/units half instead, which is unambiguous.
MG_Impl::GLImpl::PolygonOffset(3.0f, 4.0f);
MG_Impl::GLImpl::GetFloatv(GL_POLYGON_OFFSET_FACTOR, &factor);
MG_Impl::GLImpl::GetFloatv(GL_POLYGON_OFFSET_UNITS, &units);
EXPECT_FLOAT_EQ(factor, 3.0f);
EXPECT_FLOAT_EQ(units, 4.0f);
MG_Impl::GLImpl::PolygonOffsetClamp(0.0f, 0.0f, 0.0f);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
}
// GL_TEXTURE_BUFFER_BINDING (0x8C2A) is the same token as GL_TEXTURE_BUFFER; as a glGetIntegerv
// pname it asks which BUFFER object is bound there, and it had no arm at all, so
// esextcTextureBufferParameters died on its first query.
TEST_F(RenderStateTest, TextureBufferBindingAnswersTheBoundBufferName) {
DrainPendingGlErrors();
GLint binding = -1;
MG_Impl::GLImpl::GetIntegerv(GL_TEXTURE_BUFFER_BINDING, &binding);
EXPECT_EQ(binding, 0);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
}
// GL_ARB_spirv_extensions. Zero is legal and true: MobileGL relies on no SPIR-V extension, so
// glGetStringi(GL_SPIR_V_EXTENSIONS, i) is never legally reached.
TEST_F(RenderStateTest, NumSpirVExtensionsIsQueryableAndZero) {
DrainPendingGlErrors();
GLint count = -1;
MG_Impl::GLImpl::GetIntegerv(GL_NUM_SPIR_V_EXTENSIONS, &count);
EXPECT_EQ(count, 0);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
}