[Feat] (MG_State, MG_Impl, DirectGLES): implement glPatchParameteri

GL_PATCH_VERTICES decides how many vertices one tessellation patch consumes, and
glPatchParameteri was a stub - so the value stayed at the driver's default of 3 no
matter what the application asked for. KHR-GL40.texture_gather.gather-tesselation-shader
sets it to 1 and then draws a single patch: with the request dropped the draw had too
few vertices for one patch, produced nothing at all, and the case read back the clear
colour.

The value is context state on both sides and ES 3.2 spells the entry point exactly the
same way, so it is stored in the render state (where glGetIntegerv(GL_PATCH_VERTICES)
now finds it) and forwarded. Validation needs the real bound, so GL_MAX_PATCH_VERTICES
and GL_MAX_TESS_GEN_LEVEL are probed off the host driver alongside the other limits and
answered from there too; the defaults are the GL 4.0 core minimums.

KHR-GL40.texture_gather is now 75/75.
This commit is contained in:
BZLZHH
2026-08-04 10:58:14 -04:00
parent 5fce287de5
commit b95fcb7bca
14 changed files with 82 additions and 1 deletions
+5
View File
@@ -234,6 +234,8 @@ namespace MobileGL {
// drives capture from its draw recording instead (DirectVulkan). End is // drives capture from its draw recording instead (DirectVulkan). End is
// called while the frontend capture state is still active, so the backend // called while the frontend capture state is still active, so the backend
// can still see the capture program and buffer bindings. // can still see the capture program and buffer bindings.
// GL_PATCH_VERTICES; ES 3.2 spells it the same way.
void (*PatchParameteri)(GLenum pname, GLint value);
void (*BeginTransformFeedback)(GLenum primitiveMode); void (*BeginTransformFeedback)(GLenum primitiveMode);
void (*EndTransformFeedback)(); void (*EndTransformFeedback)();
// ARB_transform_feedback2. A backend that leaves these null keeps the single // ARB_transform_feedback2. A backend that leaves these null keeps the single
@@ -295,6 +297,9 @@ namespace MobileGL {
Int MaxIntegerSamples = 1; Int MaxIntegerSamples = 1;
Int MaxSamples = 1; Int MaxSamples = 1;
Int MaxSampleMaskWords = 1; Int MaxSampleMaskWords = 1;
// Tessellation limits; defaults are the GL 4.0 core minimums.
Int MaxPatchVertices = 32;
Int MaxTessGenLevel = 64;
// GL_MIN/MAX_PROGRAM_TEXTURE_GATHER_OFFSET. Defaults are the GL 4.0 core // GL_MIN/MAX_PROGRAM_TEXTURE_GATHER_OFFSET. Defaults are the GL 4.0 core
// minimums, which every ES 3.1 driver also guarantees. // minimums, which every ES 3.1 driver also guarantees.
Int MinProgramTextureGatherOffset = -8; Int MinProgramTextureGatherOffset = -8;
@@ -1034,6 +1034,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Transform feedback is captured by the real ES driver rather than // Transform feedback is captured by the real ES driver rather than
// reconstructed from the draw recording, so the frontend has to hand the // reconstructed from the draw recording, so the frontend has to hand the
// span boundaries over. // span boundaries over.
funcsTable.GL.PatchParameteri = DirectGLES::PatchParameteri;
funcsTable.GL.BeginTransformFeedback = XfbImpl::BeginTransformFeedback; funcsTable.GL.BeginTransformFeedback = XfbImpl::BeginTransformFeedback;
funcsTable.GL.EndTransformFeedback = XfbImpl::EndTransformFeedback; funcsTable.GL.EndTransformFeedback = XfbImpl::EndTransformFeedback;
funcsTable.GL.PauseTransformFeedback = XfbImpl::PauseTransformFeedback; funcsTable.GL.PauseTransformFeedback = XfbImpl::PauseTransformFeedback;
@@ -1080,6 +1081,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_dynamicParameters.MaxIntegerSamples = m_GLESCapabilities.MaxIntegerSamples; m_dynamicParameters.MaxIntegerSamples = m_GLESCapabilities.MaxIntegerSamples;
m_dynamicParameters.MaxSamples = m_GLESCapabilities.MaxSamples; m_dynamicParameters.MaxSamples = m_GLESCapabilities.MaxSamples;
m_dynamicParameters.MaxSampleMaskWords = m_GLESCapabilities.MaxSampleMaskWords; m_dynamicParameters.MaxSampleMaskWords = m_GLESCapabilities.MaxSampleMaskWords;
m_dynamicParameters.MaxPatchVertices = m_GLESCapabilities.MaxPatchVertices;
m_dynamicParameters.MaxTessGenLevel = m_GLESCapabilities.MaxTessGenLevel;
m_dynamicParameters.MinProgramTextureGatherOffset = m_GLESCapabilities.MinProgramTextureGatherOffset; m_dynamicParameters.MinProgramTextureGatherOffset = m_GLESCapabilities.MinProgramTextureGatherOffset;
m_dynamicParameters.MaxProgramTextureGatherOffset = m_GLESCapabilities.MaxProgramTextureGatherOffset; m_dynamicParameters.MaxProgramTextureGatherOffset = m_GLESCapabilities.MaxProgramTextureGatherOffset;
// Clamp the advertised sampler limits the same way the DirectVulkan backend does: per-stage // Clamp the advertised sampler limits the same way the DirectVulkan backend does: per-stage
@@ -4025,6 +4025,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
return true; return true;
} }
void PatchParameteri(GLenum pname, GLint value) {
if (g_GLESFuncs.glPatchParameteri == nullptr) return;
g_GLESFuncs.glPatchParameteri(pname, value);
}
void GenerateMipmap(GLenum target) { void GenerateMipmap(GLenum target) {
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
DebugImpl::OpenGLScopeMarker marker(__func__); DebugImpl::OpenGLScopeMarker marker(__func__);
@@ -169,6 +169,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
// begin is deferred to the first draw of the span (ES needs the capturing // begin is deferred to the first draw of the span (ES needs the capturing
// program current and the capture buffers bound), and the end also mirrors the // program current and the capture buffers bound), and the end also mirrors the
// captured bytes back into the frontend buffer shadows. // captured bytes back into the frontend buffer shadows.
void PatchParameteri(GLenum pname, GLint value);
namespace XfbImpl { namespace XfbImpl {
Bool AreTransformFeedbacksSupported(); Bool AreTransformFeedbacksSupported();
void BeginTransformFeedback(GLenum primitiveMode); void BeginTransformFeedback(GLenum primitiveMode);
@@ -11,6 +11,7 @@
#include <MG_State/GLState/Core.h> #include <MG_State/GLState/Core.h>
#include <MG_State/EGLState/Core.h> #include <MG_State/EGLState/Core.h>
#include <MG_Backend/BackendObjects.h> #include <MG_Backend/BackendObjects.h>
#include "../Getter/GL_Getter.h"
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl {
static Bool ValidateCurrentProgramForExecution(const char* functionName) { static Bool ValidateCurrentProgramForExecution(const char* functionName) {
@@ -486,6 +487,28 @@ namespace MobileGL::MG_Impl::GLImpl {
dispatchComputeIndirect(indirect); dispatchComputeIndirect(indirect);
} }
void PatchParameteri(GLenum pname, GLint value) {
if (pname != GL_PATCH_VERTICES) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "pname must be GL_PATCH_VERTICES."));
return;
}
GLint maxPatchVertices = 32;
GetIntegerv(GL_MAX_PATCH_VERTICES, &maxPatchVertices);
if (value <= 0 || value > maxPatchVertices) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"value must be in [1, GL_MAX_PATCH_VERTICES]."));
return;
}
MG_State::pGLContext->SetPatchVertices(static_cast<Uint>(value));
if (const auto patchParameteri = MG_Backend::gBackendFunctionsTable.GL.PatchParameteri) {
patchParameteri(pname, value);
}
}
void MemoryBarrier(GLbitfield barriers) { void MemoryBarrier(GLbitfield barriers) {
auto memoryBarrier = MG_Backend::gBackendFunctionsTable.GL.MemoryBarrier; auto memoryBarrier = MG_Backend::gBackendFunctionsTable.GL.MemoryBarrier;
if (!memoryBarrier) { if (!memoryBarrier) {
@@ -25,6 +25,7 @@ namespace MobileGL::MG_Impl::GLImpl {
void DrawTransformFeedbackStreamInstanced(GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); void DrawTransformFeedbackStreamInstanced(GLenum mode, GLuint id, GLuint stream, GLsizei instancecount);
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ); void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
void DispatchComputeIndirect(GLintptr indirect); void DispatchComputeIndirect(GLintptr indirect);
void PatchParameteri(GLenum pname, GLint value);
void MemoryBarrier(GLbitfield barriers); void MemoryBarrier(GLbitfield barriers);
void MemoryBarrierByRegion(GLbitfield barriers); void MemoryBarrierByRegion(GLbitfield barriers);
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride); void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
@@ -424,7 +424,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnUniformfv, GLuint program, GLint locatio
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnUniformiv, GLuint program, GLint location, GLsizei bufSize, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnUniformiv, program, location, bufSize, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnUniformiv, GLuint program, GLint location, GLsizei bufSize, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnUniformiv, program, location, bufSize, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnUniformuiv, GLuint program, GLint location, GLsizei bufSize, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnUniformuiv, program, location, bufSize, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnUniformuiv, GLuint program, GLint location, GLsizei bufSize, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnUniformuiv, program, location, bufSize, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MinSampleShading, GLfloat value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MinSampleShading, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, MinSampleShading, GLfloat value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MinSampleShading, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PatchParameteri, GLenum pname, GLint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PatchParameteri, pname, value) DECLARE_GL_FUNCTION_HEAD(void, PatchParameteri, GLenum pname, GLint value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PatchParameteri, pname, value)
DECLARE_GL_FUNCTION_HEAD(void, TexParameterIiv, GLenum target, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexParameterIiv, target, pname, params) DECLARE_GL_FUNCTION_HEAD(void, TexParameterIiv, GLenum target, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexParameterIiv, target, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, TexParameterIuiv, GLenum target, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexParameterIuiv, target, pname, params) DECLARE_GL_FUNCTION_HEAD(void, TexParameterIuiv, GLenum target, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexParameterIuiv, target, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetTexParameterIiv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTexParameterIiv, target, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetTexParameterIiv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTexParameterIiv, target, pname, params)
@@ -1917,6 +1917,15 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_MAX_SAMPLE_MASK_WORDS: case GL_MAX_SAMPLE_MASK_WORDS:
*params = dynamicParameters.MaxSampleMaskWords; *params = dynamicParameters.MaxSampleMaskWords;
break; break;
case GL_PATCH_VERTICES:
*params = static_cast<GLint>(MG_State::pGLContext->GetPatchVertices());
break;
case GL_MAX_PATCH_VERTICES:
*params = dynamicParameters.MaxPatchVertices;
break;
case GL_MAX_TESS_GEN_LEVEL:
*params = dynamicParameters.MaxTessGenLevel;
break;
case GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET: case GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET:
*params = dynamicParameters.MinProgramTextureGatherOffset; *params = dynamicParameters.MinProgramTextureGatherOffset;
break; break;
+8
View File
@@ -424,6 +424,14 @@ namespace MobileGL::MG_State {
m_renderState.SetPointSize(size); m_renderState.SetPointSize(size);
} }
void GLContext::SetPatchVertices(Uint vertices) {
m_renderState.SetPatchVertices(vertices);
}
Uint GLContext::GetPatchVertices() const {
return m_renderState.GetPatchVertices();
}
Float GLContext::GetPointSize() const { Float GLContext::GetPointSize() const {
return m_renderState.GetPointSize(); return m_renderState.GetPointSize();
} }
+2
View File
@@ -138,6 +138,8 @@ namespace MobileGL {
Float GetLineWidth() const; Float GetLineWidth() const;
void SetPointSize(Float size); void SetPointSize(Float size);
Float GetPointSize() const; Float GetPointSize() const;
void SetPatchVertices(Uint vertices);
Uint GetPatchVertices() const;
void SetPolygonOffset(Float factor, Float units); void SetPolygonOffset(Float factor, Float units);
Float GetPolygonOffsetFactor() const; Float GetPolygonOffsetFactor() const;
Float GetPolygonOffsetUnits() const; Float GetPolygonOffsetUnits() const;
@@ -154,6 +154,17 @@ namespace MobileGL {
return m_parameters.PointSize; return m_parameters.PointSize;
} }
void RenderState::SetPatchVertices(Uint vertices) {
if (m_parameters.PatchVertices == vertices) return;
m_parameters.PatchVertices = vertices;
++m_version;
}
Uint RenderState::GetPatchVertices() const {
return m_parameters.PatchVertices;
}
void RenderState::SetPolygonOffset(Float factor, Float units) { void RenderState::SetPolygonOffset(Float factor, Float units) {
if (m_parameters.PolygonOffsetFactor == factor && m_parameters.PolygonOffsetUnits == units) return; if (m_parameters.PolygonOffsetFactor == factor && m_parameters.PolygonOffsetUnits == units) return;
@@ -224,6 +224,8 @@ namespace MobileGL {
IntVec4 Viewport = IntVec4(0, 0, 0, 0); // x, y, width, height IntVec4 Viewport = IntVec4(0, 0, 0, 0); // x, y, width, height
Float LineWidth = 1.0f; Float LineWidth = 1.0f;
Float PointSize = 1.0f; Float PointSize = 1.0f;
// GL_PATCH_VERTICES: how many vertices one tessellation patch consumes.
Uint PatchVertices = 3;
Float PolygonOffsetFactor = 0.0f; Float PolygonOffsetFactor = 0.0f;
Float PolygonOffsetUnits = 0.0f; Float PolygonOffsetUnits = 0.0f;
@@ -319,6 +321,8 @@ namespace MobileGL {
Float GetLineWidth() const; Float GetLineWidth() const;
void SetPointSize(Float size); void SetPointSize(Float size);
Float GetPointSize() const; Float GetPointSize() const;
void SetPatchVertices(Uint vertices);
Uint GetPatchVertices() const;
void SetPolygonOffset(Float factor, Float units); void SetPolygonOffset(Float factor, Float units);
Float GetPolygonOffsetFactor() const; Float GetPolygonOffsetFactor() const;
Float GetPolygonOffsetUnits() const; Float GetPolygonOffsetUnits() const;
@@ -920,6 +920,8 @@ namespace MobileGL::MG_Util::BackendLoader {
// below only ever widens them. // below only ever widens them.
GLint minProgramTextureGatherOffset = -8; GLint minProgramTextureGatherOffset = -8;
GLint maxProgramTextureGatherOffset = 7; GLint maxProgramTextureGatherOffset = 7;
GLint maxPatchVertices = 32;
GLint maxTessGenLevel = 64;
glesFuncs.glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliasedLineWidthRange); glesFuncs.glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliasedLineWidthRange);
glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, smoothLineWidthRange); glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, smoothLineWidthRange);
glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &smoothLineWidthGranularity); glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &smoothLineWidthGranularity);
@@ -948,6 +950,8 @@ namespace MobileGL::MG_Util::BackendLoader {
// single test case. 1 is a spec-legal value (the minimum required), so cap // single test case. 1 is a spec-legal value (the minimum required), so cap
// to what is actually implemented instead of forwarding the raw driver limit. // to what is actually implemented instead of forwarding the raw driver limit.
maxSampleMaskWords = std::min(maxSampleMaskWords, 1); maxSampleMaskWords = std::min(maxSampleMaskWords, 1);
glesFuncs.glGetIntegerv(GL_MAX_PATCH_VERTICES, &maxPatchVertices);
glesFuncs.glGetIntegerv(GL_MAX_TESS_GEN_LEVEL, &maxTessGenLevel);
glesFuncs.glGetIntegerv(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET, &minProgramTextureGatherOffset); glesFuncs.glGetIntegerv(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET, &minProgramTextureGatherOffset);
glesFuncs.glGetIntegerv(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET, &maxProgramTextureGatherOffset); glesFuncs.glGetIntegerv(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET, &maxProgramTextureGatherOffset);
// A driver that leaves the probe untouched (pre-ES 3.1, or an ignored enum) must not // A driver that leaves the probe untouched (pre-ES 3.1, or an ignored enum) must not
@@ -1037,6 +1041,8 @@ namespace MobileGL::MG_Util::BackendLoader {
caps.MaxIntegerSamples = maxIntegerSamples; caps.MaxIntegerSamples = maxIntegerSamples;
caps.MaxSamples = maxSamples; caps.MaxSamples = maxSamples;
caps.MaxSampleMaskWords = maxSampleMaskWords; caps.MaxSampleMaskWords = maxSampleMaskWords;
caps.MaxPatchVertices = maxPatchVertices;
caps.MaxTessGenLevel = maxTessGenLevel;
caps.MinProgramTextureGatherOffset = minProgramTextureGatherOffset; caps.MinProgramTextureGatherOffset = minProgramTextureGatherOffset;
caps.MaxProgramTextureGatherOffset = maxProgramTextureGatherOffset; caps.MaxProgramTextureGatherOffset = maxProgramTextureGatherOffset;
caps.MaxTextureImageUnits = maxTextureImageUnits; caps.MaxTextureImageUnits = maxTextureImageUnits;
@@ -1102,6 +1102,8 @@ namespace MobileGL {
Int MaxIntegerSamples = 1; Int MaxIntegerSamples = 1;
Int MaxSamples = 1; Int MaxSamples = 1;
Int MaxSampleMaskWords = 1; Int MaxSampleMaskWords = 1;
Int MaxPatchVertices = 32;
Int MaxTessGenLevel = 64;
Int MinProgramTextureGatherOffset = -8; Int MinProgramTextureGatherOffset = -8;
Int MaxProgramTextureGatherOffset = 7; Int MaxProgramTextureGatherOffset = 7;
Int MaxTextureImageUnits = 32; Int MaxTextureImageUnits = 32;