[Feat] (MG_Impl/GLImpl): implement 8 packed glVertexAttribP*ui current-value setters

glVertexAttribP{1,2,3,4}ui and their *uiv forms set the CURRENT generic
vertex attribute value from a packed 2_10_10_10_REV word (they are the
packed members of the immediate VertexAttrib* family, not the array-format
path), so they funnel into SetCurrentVertexAttributeFloat and reuse the
existing index validation.

- Add DecodePacked2101010: unpacks x=[0..9], y=[10..19], z=[20..29] (10-bit)
  and w=[30..31] (2-bit) from one 32-bit word. Signed fields are two's-
  complement (sign-extended per width); normalized conversion uses the
  GL 3.3 (2c+1)/(2^b-1) form (10-bit /1023, 2-bit /3), matching the
  existing NormalizeSigned* helpers -- NOT the GL 4.2 clamp form.
- type accepts only GL_INT_2_10_10_10_REV / GL_UNSIGNED_INT_2_10_10_10_REV
  (GL_INVALID_ENUM otherwise; the 4.4-era 10F_11F_11F_REV is not legal in
  3.3). P1/P2/P3 consume the first 1/2/3 components; the rest take the
  (0,0,0,1) defaults and are cleared each call. The *uiv forms dereference
  a single packed word, not an array.

Tests: 4 VertexArrayTest cases (unsigned decode, signed GL-3.3 formula,
component-count/defaults, type/index/uiv validation). The signed test is
mutation-verified: z==0 -> 1/1023 fails against the GL 4.2 form.
VertexArrayTest 38/38, SanityTest 30/30.
This commit is contained in:
2026-07-10 23:07:13 -04:00
parent 3ff8cafac6
commit 0cada09aa7
4 changed files with 250 additions and 8 deletions
@@ -871,14 +871,14 @@ DECLARE_GL_FUNCTION_STUB_HEAD(GLint, GetFragDataIndex, GLuint program, const GLc
DECLARE_GL_FUNCTION_HEAD(void, QueryCounter, GLuint id, GLenum target) DECLARE_GL_FUNCTION_END_NO_RETURN(void, QueryCounter, id, target)
DECLARE_GL_FUNCTION_HEAD(void, GetQueryObjecti64v, GLuint id, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryObjecti64v, id, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetQueryObjectui64v, GLuint id, GLenum pname, GLuint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryObjectui64v, id, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribP1ui, GLuint index, GLenum type, GLboolean normalized, GLuint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribP1ui, index, type, normalized, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribP1uiv, GLuint index, GLenum type, GLboolean normalized, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribP1uiv, index, type, normalized, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribP2ui, GLuint index, GLenum type, GLboolean normalized, GLuint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribP2ui, index, type, normalized, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribP2uiv, GLuint index, GLenum type, GLboolean normalized, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribP2uiv, index, type, normalized, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribP3ui, GLuint index, GLenum type, GLboolean normalized, GLuint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribP3ui, index, type, normalized, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribP3uiv, GLuint index, GLenum type, GLboolean normalized, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribP3uiv, index, type, normalized, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribP4ui, GLuint index, GLenum type, GLboolean normalized, GLuint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribP4ui, index, type, normalized, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribP4uiv, GLuint index, GLenum type, GLboolean normalized, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribP4uiv, index, type, normalized, value)
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribP1ui, GLuint index, GLenum type, GLboolean normalized, GLuint value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribP1ui, index, type, normalized, value)
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribP1uiv, GLuint index, GLenum type, GLboolean normalized, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribP1uiv, index, type, normalized, value)
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribP2ui, GLuint index, GLenum type, GLboolean normalized, GLuint value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribP2ui, index, type, normalized, value)
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribP2uiv, GLuint index, GLenum type, GLboolean normalized, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribP2uiv, index, type, normalized, value)
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribP3ui, GLuint index, GLenum type, GLboolean normalized, GLuint value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribP3ui, index, type, normalized, value)
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribP3uiv, GLuint index, GLenum type, GLboolean normalized, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribP3uiv, index, type, normalized, value)
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribP4ui, GLuint index, GLenum type, GLboolean normalized, GLuint value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribP4ui, index, type, normalized, value)
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribP4uiv, GLuint index, GLenum type, GLboolean normalized, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribP4uiv, index, type, normalized, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexP2ui, GLenum type, GLuint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexP2ui, type, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexP2uiv, GLenum type, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexP2uiv, type, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexP3ui, GLenum type, GLuint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexP3ui, type, value)
@@ -12,6 +12,7 @@
#include <MG_State/GLState/Core.h>
#include <MG_State/GLState/ErrorState/Error.h>
#include <MG_Util/Converters/GLToMG/DataTypeConverter.h>
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
#include <MG_Util/Converters/MGToGL/DataTypeConverter.h>
namespace MobileGL::MG_Impl::GLImpl {
@@ -41,6 +42,41 @@ namespace MobileGL::MG_Impl::GLImpl {
}
// (b = 8 unsigned normalization is VertexAttrib4Nub's x * (1/255).)
// Sign-extend a `bits`-wide two's-complement field held in the low bits of `field`.
constexpr GLint SignExtendField(GLuint field, int bits) {
const GLuint signBit = 1u << (bits - 1);
return (field & signBit) ? static_cast<GLint>(field | (~0u << bits)) : static_cast<GLint>(field);
}
// Decode one GL_INT_/GL_UNSIGNED_INT_2_10_10_10_REV packed word into four float components.
// The _REV layout packs x in bits [0..9], y in [10..19], z in [20..29], w in [30..31]; x/y/z
// are 10-bit fields and w is a 2-bit field. Signed fields are two's-complement, and normalized
// conversion uses the GL 3.3 (2c+1)/(2^b-1) form (matching NormalizeSigned* above), NOT the
// GL 4.2 clamp form.
Array<GLfloat, 4> DecodePacked2101010(GLuint value, bool signedType, bool normalized) {
const GLuint fx = value & 0x3FFu;
const GLuint fy = (value >> 10) & 0x3FFu;
const GLuint fz = (value >> 20) & 0x3FFu;
const GLuint fw = (value >> 30) & 0x3u;
if (signedType) {
const GLint sx = SignExtendField(fx, 10);
const GLint sy = SignExtendField(fy, 10);
const GLint sz = SignExtendField(fz, 10);
const GLint sw = SignExtendField(fw, 2);
if (normalized) {
return {(2 * sx + 1) / 1023.0f, (2 * sy + 1) / 1023.0f, (2 * sz + 1) / 1023.0f,
(2 * sw + 1) / 3.0f};
}
return {static_cast<GLfloat>(sx), static_cast<GLfloat>(sy), static_cast<GLfloat>(sz),
static_cast<GLfloat>(sw)};
}
if (normalized) {
return {fx / 1023.0f, fy / 1023.0f, fz / 1023.0f, fw / 3.0f};
}
return {static_cast<GLfloat>(fx), static_cast<GLfloat>(fy), static_cast<GLfloat>(fz),
static_cast<GLfloat>(fw)};
}
static bool ValidateCurrentVertexAttribIndex(GLuint index, const char* funcName) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return false;
if (index == 0) {
@@ -499,6 +535,87 @@ namespace MobileGL::MG_Impl::GLImpl {
VertexAttribI4ui(index, v[0], v[1], v[2], v[3]);
}
// Shared body for glVertexAttribP{1,2,3,4}ui(v). These set the CURRENT generic vertex attribute
// value (they are the packed members of the immediate VertexAttrib* family, not the array-format
// path), so they take the float current-value funnel. The single packed word is always fully
// decoded, but only the first `componentCount` components are written; the rest keep the generic
// attribute defaults (0, 0, 0, 1). type must be one of the two 2_10_10_10_REV packed enums.
static void VertexAttribP_Common(GLuint index, GLenum type, GLboolean normalized, GLuint value,
int componentCount, const char* funcName) {
if (!ValidateCurrentVertexAttribIndex(index, funcName)) return;
bool signedType;
if (type == GL_INT_2_10_10_10_REV) {
signedType = true;
} else if (type == GL_UNSIGNED_INT_2_10_10_10_REV) {
signedType = false;
} else {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName,
"glVertexAttribP*ui type must be GL_INT_2_10_10_10_REV or "
"GL_UNSIGNED_INT_2_10_10_10_REV; got " +
MG_Util::ConvertGLEnumToString(type) + "."));
return;
}
const Array<GLfloat, 4> decoded = DecodePacked2101010(value, signedType, normalized == GL_TRUE);
Array<GLfloat, 4> out = {0.0f, 0.0f, 0.0f, 1.0f};
for (int i = 0; i < componentCount; ++i) out[i] = decoded[i];
MG_State::pGLContext->SetCurrentVertexAttributeFloat(index, out);
}
void VertexAttribP1ui(GLuint index, GLenum type, GLboolean normalized, GLuint value) {
VertexAttribP_Common(index, type, normalized, value, 1, __func__);
}
void VertexAttribP2ui(GLuint index, GLenum type, GLboolean normalized, GLuint value) {
VertexAttribP_Common(index, type, normalized, value, 2, __func__);
}
void VertexAttribP3ui(GLuint index, GLenum type, GLboolean normalized, GLuint value) {
VertexAttribP_Common(index, type, normalized, value, 3, __func__);
}
void VertexAttribP4ui(GLuint index, GLenum type, GLboolean normalized, GLuint value) {
VertexAttribP_Common(index, type, normalized, value, 4, __func__);
}
// The *uiv forms dereference a pointer to a SINGLE packed GLuint (never an array of N words).
void VertexAttribP1uiv(GLuint index, GLenum type, GLboolean normalized, const GLuint* value) {
if (!value) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "value pointer cannot be null."));
return;
}
VertexAttribP_Common(index, type, normalized, value[0], 1, __func__);
}
void VertexAttribP2uiv(GLuint index, GLenum type, GLboolean normalized, const GLuint* value) {
if (!value) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "value pointer cannot be null."));
return;
}
VertexAttribP_Common(index, type, normalized, value[0], 2, __func__);
}
void VertexAttribP3uiv(GLuint index, GLenum type, GLboolean normalized, const GLuint* value) {
if (!value) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "value pointer cannot be null."));
return;
}
VertexAttribP_Common(index, type, normalized, value[0], 3, __func__);
}
void VertexAttribP4uiv(GLuint index, GLenum type, GLboolean normalized, const GLuint* value) {
if (!value) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "value pointer cannot be null."));
return;
}
VertexAttribP_Common(index, type, normalized, value[0], 4, __func__);
}
void VertexAttrib4Nub(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w) {
constexpr float kInv255 = 1.0f / 255.0f;
VertexAttrib4f(index, x * kInv255, y * kInv255, z * kInv255, w * kInv255);
@@ -23,6 +23,16 @@ namespace MobileGL::MG_Impl::GLImpl {
void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
void VertexAttribI4iv(GLuint index, const GLint* v);
void VertexAttribI4uiv(GLuint index, const GLuint* v);
// Packed current-value setters (GL_INT_/GL_UNSIGNED_INT_2_10_10_10_REV). Decode one packed word
// into the first 1/2/3/4 components of the generic attribute's float current value.
void VertexAttribP1ui(GLuint index, GLenum type, GLboolean normalized, GLuint value);
void VertexAttribP2ui(GLuint index, GLenum type, GLboolean normalized, GLuint value);
void VertexAttribP3ui(GLuint index, GLenum type, GLboolean normalized, GLuint value);
void VertexAttribP4ui(GLuint index, GLenum type, GLboolean normalized, GLuint value);
void VertexAttribP1uiv(GLuint index, GLenum type, GLboolean normalized, const GLuint* value);
void VertexAttribP2uiv(GLuint index, GLenum type, GLboolean normalized, const GLuint* value);
void VertexAttribP3uiv(GLuint index, GLenum type, GLboolean normalized, const GLuint* value);
void VertexAttribP4uiv(GLuint index, GLenum type, GLboolean normalized, const GLuint* value);
void VertexAttrib4Nub(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);
void VertexAttrib4Nubv(GLuint index, const GLubyte* v);
void VertexAttrib4ubv(GLuint index, const GLubyte* v);