mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 20:28:32 +09:00
[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:
@@ -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);
|
||||
|
||||
@@ -1012,3 +1012,118 @@ TEST_F(GeneralVertexArrayTest, CurrentAttrib_GetVertexAttribdv) {
|
||||
EXPECT_DOUBLE_EQ(d2[0], 9.0);
|
||||
}
|
||||
|
||||
// Family E: packed glVertexAttribP*ui. 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.
|
||||
// E1: unsigned decode (normalized c/(2^b-1), and unnormalized cast-to-float).
|
||||
TEST_F(GeneralVertexArrayTest, CurrentAttrib_PackedUnsigned) {
|
||||
CreateVAO();
|
||||
GLfloat out[4];
|
||||
|
||||
// x=1023, y=0, z=512, w=3.
|
||||
const GLuint packed = 1023u | (0u << 10) | (512u << 20) | (3u << 30); // 0xE00003FF
|
||||
VertexAttribP4ui(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, packed);
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
EXPECT_FLOAT_EQ(out[0], 1.0f); // 1023/1023
|
||||
EXPECT_FLOAT_EQ(out[1], 0.0f); // 0/1023
|
||||
EXPECT_FLOAT_EQ(out[2], 512.0f / 1023.0f); // 10-bit divisor, NOT 1023 for w
|
||||
EXPECT_FLOAT_EQ(out[3], 1.0f); // 3/3 (2-bit divisor)
|
||||
|
||||
// Unnormalized: the field values are cast straight to float, no scaling.
|
||||
VertexAttribP4ui(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, packed);
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_FLOAT_EQ(out[0], 1023.0f);
|
||||
EXPECT_FLOAT_EQ(out[2], 512.0f);
|
||||
EXPECT_FLOAT_EQ(out[3], 3.0f);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
// E2: signed normalization uses the GL 3.3 (2c+1)/(2^b-1) form -- z==0 -> 1/1023 (NOT 0.0), which is
|
||||
// exactly what the GL 4.2 c/(2^(b-1)-1) rule would get wrong.
|
||||
TEST_F(GeneralVertexArrayTest, CurrentAttrib_PackedSignedUsesGl33Formula) {
|
||||
CreateVAO();
|
||||
GLfloat out[4];
|
||||
|
||||
// x=511 (max +), y=-512 (min, 10-bit 0x200), z=0, w=1 (max + for 2-bit).
|
||||
const GLuint packed = 0x1FFu | (0x200u << 10) | (0u << 20) | (1u << 30);
|
||||
VertexAttribP4ui(1, GL_INT_2_10_10_10_REV, GL_TRUE, packed);
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
EXPECT_FLOAT_EQ(out[0], 1.0f); // (2*511+1)/1023 = 1023/1023
|
||||
EXPECT_FLOAT_EQ(out[1], -1.0f); // (2*-512+1)/1023 = -1023/1023
|
||||
EXPECT_FLOAT_EQ(out[2], 1.0f / 1023.0f); // (2*0+1)/1023 -- 4.2 rule would give 0.0
|
||||
EXPECT_FLOAT_EQ(out[3], 1.0f); // (2*1+1)/3 = 3/3
|
||||
|
||||
// 2-bit signed minimum: w field = 0b10 = -2 -> (2*-2+1)/3 = -1.0.
|
||||
VertexAttribP4ui(1, GL_INT_2_10_10_10_REV, GL_TRUE, 2u << 30);
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_FLOAT_EQ(out[3], -1.0f);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
// E3: P1/P2/P3 consume the first 1/2/3 components from the single packed word; unconsumed components
|
||||
// take the (0,0,0,1) defaults, and a shorter call clears stale components from a prior P4 to the slot.
|
||||
TEST_F(GeneralVertexArrayTest, CurrentAttrib_PackedComponentCountAndDefaults) {
|
||||
CreateVAO();
|
||||
GLfloat out[4];
|
||||
|
||||
VertexAttribP1ui(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 5u);
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_FLOAT_EQ(out[0], 5.0f);
|
||||
EXPECT_FLOAT_EQ(out[1], 0.0f);
|
||||
EXPECT_FLOAT_EQ(out[2], 0.0f);
|
||||
EXPECT_FLOAT_EQ(out[3], 1.0f);
|
||||
|
||||
VertexAttribP2ui(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 5u | (7u << 10));
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_FLOAT_EQ(out[1], 7.0f);
|
||||
EXPECT_FLOAT_EQ(out[2], 0.0f);
|
||||
EXPECT_FLOAT_EQ(out[3], 1.0f);
|
||||
|
||||
VertexAttribP3ui(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 5u | (7u << 10) | (9u << 20));
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_FLOAT_EQ(out[2], 9.0f);
|
||||
EXPECT_FLOAT_EQ(out[3], 1.0f);
|
||||
|
||||
// Set all four, then a P1 must reset z and w back to the defaults.
|
||||
VertexAttribP4ui(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 1u | (2u << 10) | (3u << 20) | (1u << 30));
|
||||
VertexAttribP1ui(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 5u);
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_FLOAT_EQ(out[0], 5.0f);
|
||||
EXPECT_FLOAT_EQ(out[2], 0.0f); // not stale 3
|
||||
EXPECT_FLOAT_EQ(out[3], 1.0f); // not stale 1-from-w
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
// E4: type/index validation and the uiv single-word form.
|
||||
TEST_F(GeneralVertexArrayTest, CurrentAttrib_PackedValidation) {
|
||||
CreateVAO();
|
||||
GLfloat out[4];
|
||||
|
||||
// A non-packed type is GL_INVALID_ENUM and changes nothing.
|
||||
VertexAttribP4ui(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 42u);
|
||||
VertexAttribP4ui(1, GL_INT, GL_FALSE, 7u); // GL_INT is not a legal packed type
|
||||
EXPECT_EQ(GetError(), GL_INVALID_ENUM);
|
||||
GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_FLOAT_EQ(out[0], 42.0f); // unchanged by the failed call
|
||||
|
||||
// Attribute 0 is rejected by MobileGL policy (GL_INVALID_OPERATION).
|
||||
VertexAttribP4ui(0, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 1u);
|
||||
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
|
||||
|
||||
// Out-of-range index -> GL_INVALID_VALUE.
|
||||
VertexAttribP4ui(VertexArrayImpl::GetMaxVertexAttribs(), GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 1u);
|
||||
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||
|
||||
// The uiv form dereferences a single packed word.
|
||||
const GLuint word = 1023u; // x=1023 normalized -> 1.0
|
||||
VertexAttribP1uiv(2, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE, &word);
|
||||
GetVertexAttribfv(2, GL_CURRENT_VERTEX_ATTRIB, out);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
EXPECT_FLOAT_EQ(out[0], 1.0f);
|
||||
|
||||
// Null uiv pointer -> GL_INVALID_VALUE.
|
||||
VertexAttribP4uiv(1, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, nullptr);
|
||||
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user