GLdc/tests/test_vertex_formats.h
2026-06-22 09:05:44 +01:00

314 lines
10 KiB
C++

#pragma once
#include "tools/test.h"
#include "tools/gl_test.h"
#include <stdint.h>
#include <vector>
#include <GL/gl.h>
#include <GL/glkos.h>
#include "GL/private.h"
#include "GL/state.h"
#include "containers/aligned_vector.h"
/* =========================================================================
* VertexFormatTests
*
* Coverage for the vertex-position attribute readers used by glDrawArrays /
* glDrawElements (GL/attributes.c). Each supported pointer type (float, short,
* int, byte, double) and size (2 / 3) is exercised, and the *submitted* vertex
* positions are compared against an equivalent GL_FLOAT draw.
*
* This pins down the per-type interpretation, e.g. that GL_SHORT/GL_INT are
* read as raw values while GL_UNSIGNED_BYTE is normalised by 1/255, so a
* regression in any reader is caught without depending on the exact viewport
* matrix maths.
* =========================================================================*/
class VertexFormatTests : public GLTestCase {
public:
void set_up() {
GLTestCase::set_up();
glEnableClientState(GL_VERTEX_ARRAY);
}
/* Empty OP_LIST and force a fresh poly header on the next draw. */
static void reset_list() {
aligned_vector_clear(&OP_LIST.vector);
_glGPUStateMarkDirty();
}
/* All vertex-flagged entries currently in OP_LIST (headers skipped). */
static std::vector<Vertex> captured() {
std::vector<Vertex> out;
uint32_t n = aligned_vector_size(&OP_LIST.vector);
for(uint32_t i = 0; i < n; ++i) {
Vertex* v = (Vertex*) aligned_vector_at(&OP_LIST.vector, i);
if(v->flags == GPU_CMD_VERTEX || v->flags == GPU_CMD_VERTEX_EOL) {
out.push_back(*v);
}
}
return out;
}
void assert_positions_match(const std::vector<Vertex>& a, const std::vector<Vertex>& b) {
assert_equal(a.size(), b.size());
for(size_t i = 0; i < a.size(); ++i) {
assert_close(a[i].xyz[0], b[i].xyz[0], 0.01f);
assert_close(a[i].xyz[1], b[i].xyz[1], 0.01f);
assert_close(a[i].xyz[2], b[i].xyz[2], 0.01f);
assert_close(a[i].w, b[i].w, 0.01f);
}
}
/* --------------------------------------------------------- float (ref) */
void test_float_size3_basic_submission() {
GLfloat verts[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
};
reset_list();
glVertexPointer(3, GL_FLOAT, 0, verts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> v = captured();
assert_equal(v.size(), (size_t) 3);
/* w must be 1 for an identity transform of a z=0 triangle. */
assert_close(v[0].w, 1.0f, 0.001f);
}
/* --------------------------------------------------------- short == float */
void test_short_matches_equivalent_float() {
GLshort sverts[] = { 2, 3, 5, 7, 11, 1 }; /* size 2 */
GLfloat fverts[] = { 2, 3, 5, 7, 11, 1 };
reset_list();
glVertexPointer(2, GL_FLOAT, 0, fverts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> fref = captured();
reset_list();
glVertexPointer(2, GL_SHORT, 0, sverts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> sres = captured();
assert_positions_match(fref, sres);
}
/* --------------------------------------------------------- int == float */
void test_int_matches_equivalent_float() {
GLint iverts[] = { 1, 4, 6, 2, 3, 9 };
GLfloat fverts[] = { 1, 4, 6, 2, 3, 9 };
reset_list();
glVertexPointer(2, GL_FLOAT, 0, fverts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> fref = captured();
reset_list();
glVertexPointer(2, GL_INT, 0, iverts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> ires = captured();
assert_positions_match(fref, ires);
}
/* --------------------------------------------------------- double == float */
void test_double_matches_equivalent_float() {
GLdouble dverts[] = { -0.5, -0.5, 0.5, -0.5, 0.0, 0.5 };
GLfloat fverts[] = { -0.5f, -0.5f, 0.5f, -0.5f, 0.0f, 0.5f };
reset_list();
glVertexPointer(2, GL_FLOAT, 0, fverts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> fref = captured();
reset_list();
glVertexPointer(2, GL_DOUBLE, 0, dverts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> dres = captured();
assert_positions_match(fref, dres);
}
/* GL_UNSIGNED_BYTE positions are normalised by 1/255. */
void test_ubyte_matches_normalized_float() {
GLubyte bverts[] = { 10, 20, 30, 40, 50, 60 };
GLfloat fverts[] = {
10.0f/255.0f, 20.0f/255.0f,
30.0f/255.0f, 40.0f/255.0f,
50.0f/255.0f, 60.0f/255.0f,
};
reset_list();
glVertexPointer(2, GL_FLOAT, 0, fverts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> fref = captured();
reset_list();
glVertexPointer(2, GL_UNSIGNED_BYTE, 0, bverts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> bres = captured();
assert_positions_match(fref, bres);
}
/* --------------------------------------------------------- size 2 vs 3 */
/* A size-2 pointer must produce z == 0 (and therefore match a size-3 draw
* whose z components are all zero). */
void test_size2_implies_zero_z() {
GLfloat v2[] = { -0.5f, -0.5f, 0.5f, -0.5f, 0.0f, 0.5f };
GLfloat v3[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
};
reset_list();
glVertexPointer(3, GL_FLOAT, 0, v3);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> r3 = captured();
reset_list();
glVertexPointer(2, GL_FLOAT, 0, v2);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> r2 = captured();
assert_positions_match(r3, r2);
}
/* --------------------------------------------------------- stride */
/* An interleaved array with padding between vertices must read the same
* positions as a tightly-packed one. */
void test_custom_stride_is_respected() {
GLfloat tight[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
};
/* Same positions, but each followed by 2 padding floats (stride 20). */
GLfloat padded[] = {
-0.5f, -0.5f, 0.0f, 99.0f, 99.0f,
0.5f, -0.5f, 0.0f, 99.0f, 99.0f,
0.0f, 0.5f, 0.0f, 99.0f, 99.0f,
};
reset_list();
glVertexPointer(3, GL_FLOAT, 0, tight);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> rt = captured();
reset_list();
glVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), padded);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> rp = captured();
assert_positions_match(rt, rp);
}
/* --------------------------------------------------------- glDrawElements */
void test_draw_elements_matches_draw_arrays() {
GLfloat verts[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
};
GLubyte indices[] = { 0, 1, 2 };
reset_list();
glVertexPointer(3, GL_FLOAT, 0, verts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> da = captured();
reset_list();
glVertexPointer(3, GL_FLOAT, 0, verts);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, indices);
std::vector<Vertex> de = captured();
assert_positions_match(da, de);
}
/* glDrawElements honours the index order. */
void test_draw_elements_reorders_vertices() {
GLfloat verts[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
};
GLubyte forward[] = { 0, 1, 2 };
GLubyte reversed[] = { 2, 1, 0 };
reset_list();
glVertexPointer(3, GL_FLOAT, 0, verts);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, forward);
std::vector<Vertex> f = captured();
reset_list();
glVertexPointer(3, GL_FLOAT, 0, verts);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, reversed);
std::vector<Vertex> r = captured();
assert_equal(f.size(), (size_t) 3);
/* First of forward == last of reversed. */
assert_close(f[0].xyz[0], r[2].xyz[0], 0.01f);
assert_close(f[0].xyz[1], r[2].xyz[1], 0.01f);
assert_close(f[2].xyz[0], r[0].xyz[0], 0.01f);
}
/* --------------------------------------------------------- immediate mode */
/* glVertex2f(x,y) must equal glVertex3f(x,y,0). */
void test_immediate_vertex2f_equals_vertex3f_zero_z() {
reset_list();
glBegin(GL_TRIANGLES);
glVertex3f(-0.5f, -0.5f, 0.0f);
glVertex3f( 0.5f, -0.5f, 0.0f);
glVertex3f( 0.0f, 0.5f, 0.0f);
glEnd();
std::vector<Vertex> v3 = captured();
reset_list();
glBegin(GL_TRIANGLES);
glVertex2f(-0.5f, -0.5f);
glVertex2f( 0.5f, -0.5f);
glVertex2f( 0.0f, 0.5f);
glEnd();
std::vector<Vertex> v2 = captured();
assert_positions_match(v3, v2);
}
/* Immediate mode must produce the same submission as an equivalent
* vertex-array draw. */
void test_immediate_matches_vertex_array() {
GLfloat verts[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
};
reset_list();
glVertexPointer(3, GL_FLOAT, 0, verts);
glDrawArrays(GL_TRIANGLES, 0, 3);
std::vector<Vertex> arr = captured();
reset_list();
glBegin(GL_TRIANGLES);
glVertex3f(-0.5f, -0.5f, 0.0f);
glVertex3f( 0.5f, -0.5f, 0.0f);
glVertex3f( 0.0f, 0.5f, 0.0f);
glEnd();
std::vector<Vertex> imm = captured();
assert_positions_match(arr, imm);
}
};