From 8743b0a5b99c6639829916f5dbd888ce914fa67a Mon Sep 17 00:00:00 2001 From: Luke Benstead Date: Mon, 20 Apr 2026 22:23:31 +0100 Subject: [PATCH] Add tests --- GL/platform.h | 2 +- GL/platforms/software.h | 2 + GL/private.h | 2 + GL/state.h | 8 + tests/test_glcolor.h | 576 +++++++++++++++++++++++++++++ tests/test_nearz_clipping.h | 11 + tests/test_pvr_vertex_submission.h | 522 ++++++++++++++++++++++++++ tools/test.h | 1 + 8 files changed, 1123 insertions(+), 1 deletion(-) create mode 100644 tests/test_glcolor.h create mode 100644 tests/test_nearz_clipping.h create mode 100644 tests/test_pvr_vertex_submission.h diff --git a/GL/platform.h b/GL/platform.h index b1428a3..4e9e23b 100644 --- a/GL/platform.h +++ b/GL/platform.h @@ -420,7 +420,7 @@ static inline void CompilePolyHeader(PolyHeader *dst, const PolyContext *src) { if(src->txr.enable == GPU_TEXTURE_DISABLE) { /* Texturing was disabled so we simulate it with a white texture */ if(!DEFAULT_TEXTURE) { - DEFAULT_TEXTURE = GPUMemoryAlloc(8 * 8 * 2); + DEFAULT_TEXTURE = (uint8_t*) GPUMemoryAlloc(8 * 8 * 2); for(int i = 0; i < 8 * 8 * 2; ++i) { DEFAULT_TEXTURE[i] = 0xff; } diff --git a/GL/platforms/software.h b/GL/platforms/software.h index e407e9d..ee14b47 100644 --- a/GL/platforms/software.h +++ b/GL/platforms/software.h @@ -32,8 +32,10 @@ #define VEC3_DOT(x1, y1, z1, x2, y2, z2, d) \ d = (x1 * x2) + (y1 * y2) + (z1 * z2) +#ifndef __cplusplus struct PolyHeader; struct PolyContext; +#endif void UploadMatrix4x4(const Matrix4x4* mat); void MultiplyMatrix4x4(const Matrix4x4* mat); diff --git a/GL/private.h b/GL/private.h index 3ef7577..d86ca3e 100644 --- a/GL/private.h +++ b/GL/private.h @@ -272,7 +272,9 @@ typedef enum { #define G8IDX 2 #define B8IDX 3 +#ifndef __cplusplus struct SubmissionTarget; +#endif PolyList* _glOpaquePolyList(); PolyList* _glPunchThruPolyList(); diff --git a/GL/state.h b/GL/state.h index f67e117..0ca13cf 100644 --- a/GL/state.h +++ b/GL/state.h @@ -2,6 +2,10 @@ #include "../include/GL/gl.h" +#ifdef __cplusplus +extern "C" { +#endif + GLboolean _glNearZClippingEnabled(); GLboolean _glGPUStateIsDirty(); void _glGPUStateMarkClean(); @@ -14,3 +18,7 @@ float* _glCurrentTexCoord1(); GLfloat _glGetHalfPointSize(); GLfloat _glGetHalfLineWidth(); + +#ifdef __cplusplus +} +#endif diff --git a/tests/test_glcolor.h b/tests/test_glcolor.h new file mode 100644 index 0000000..fc92515 --- /dev/null +++ b/tests/test_glcolor.h @@ -0,0 +1,576 @@ +#pragma once + +#include "tools/test.h" + +#include +#include +#include + +/* Internal headers for direct state inspection */ +#include "GL/state.h" +#include "GL/private.h" + +/* ========================================================================= + * GlColorTests + * + * Tests for glColor4f / glColor3f / glColor4ub / glColor3ub and their + * vector variants. Each call stores a normalised RGBA colour into the + * internal current-colour array in ARGB component order: + * index 0 = alpha (A8IDX) + * index 1 = red (R8IDX) + * index 2 = green (G8IDX) + * index 3 = blue (B8IDX) + * =========================================================================*/ +class GlColorTests : public test::TestCase { +public: + void set_up() { + GLdcConfig config; + glKosInitConfig(&config); + config.texture_twiddle = false; + glKosInitEx(&config); + /* _glInitContext does not reset current_color, so do it explicitly + * here so every test starts from a known white default. */ + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } + + void tear_down() { + glKosShutdown(); + } + + /* After set_up the current colour must be white (1,1,1,1). */ + void test_default_color_is_white() { + float* color = _glCurrentColor(); + assert_close(color[A8IDX], 1.0f, 0.001f); + assert_close(color[R8IDX], 1.0f, 0.001f); + assert_close(color[G8IDX], 1.0f, 0.001f); + assert_close(color[B8IDX], 1.0f, 0.001f); + } + + /* glColor4f must store each component at its correct ARGB slot. */ + void test_color4f_sets_rgba() { + glColor4f(0.25f, 0.5f, 0.75f, 0.9f); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 0.25f, 0.001f); + assert_close(color[G8IDX], 0.50f, 0.001f); + assert_close(color[B8IDX], 0.75f, 0.001f); + assert_close(color[A8IDX], 0.90f, 0.001f); + } + + /* Sanity check for a pure-red colour. */ + void test_color4f_pure_red() { + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 1.0f, 0.001f); + assert_close(color[G8IDX], 0.0f, 0.001f); + assert_close(color[B8IDX], 0.0f, 0.001f); + assert_close(color[A8IDX], 1.0f, 0.001f); + } + + /* Sanity check for a pure-blue colour. */ + void test_color4f_pure_blue() { + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 0.0f, 0.001f); + assert_close(color[G8IDX], 0.0f, 0.001f); + assert_close(color[B8IDX], 1.0f, 0.001f); + assert_close(color[A8IDX], 1.0f, 0.001f); + } + + /* The second call must completely overwrite the first. */ + void test_color4f_overwrites_previous_color() { + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + glColor4f(0.0f, 1.0f, 0.0f, 0.5f); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 0.0f, 0.001f); + assert_close(color[G8IDX], 1.0f, 0.001f); + assert_close(color[B8IDX], 0.0f, 0.001f); + assert_close(color[A8IDX], 0.5f, 0.001f); + } + + /* glColor3f sets RGB and forces alpha to 1.0. */ + void test_color3f_sets_rgb_and_forces_alpha_to_one() { + glColor3f(0.25f, 0.5f, 0.75f); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 0.25f, 0.001f); + assert_close(color[G8IDX], 0.50f, 0.001f); + assert_close(color[B8IDX], 0.75f, 0.001f); + assert_close(color[A8IDX], 1.00f, 0.001f); + } + + /* After a prior glColor4f with alpha=0, glColor3f must reset alpha to 1. */ + void test_color3f_resets_alpha_to_one() { + glColor4f(0.5f, 0.5f, 0.5f, 0.0f); + glColor3f(1.0f, 0.0f, 0.0f); + float* color = _glCurrentColor(); + assert_close(color[A8IDX], 1.0f, 0.001f); + } + + /* glColor4ub must convert each byte to a normalised float. */ + void test_color4ub_converts_bytes_to_normalized_floats() { + glColor4ub(255, 128, 64, 192); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 255.0f / 255.0f, 0.005f); + assert_close(color[G8IDX], 128.0f / 255.0f, 0.005f); + assert_close(color[B8IDX], 64.0f / 255.0f, 0.005f); + assert_close(color[A8IDX], 192.0f / 255.0f, 0.005f); + } + + /* 255 must map to exactly 1.0. */ + void test_color4ub_max_values_map_to_one() { + glColor4ub(255, 255, 255, 255); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 1.0f, 0.001f); + assert_close(color[G8IDX], 1.0f, 0.001f); + assert_close(color[B8IDX], 1.0f, 0.001f); + assert_close(color[A8IDX], 1.0f, 0.001f); + } + + /* 0 must map to exactly 0.0. */ + void test_color4ub_zero_values_map_to_zero() { + glColor4ub(0, 0, 0, 0); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 0.0f, 0.001f); + assert_close(color[G8IDX], 0.0f, 0.001f); + assert_close(color[B8IDX], 0.0f, 0.001f); + assert_close(color[A8IDX], 0.0f, 0.001f); + } + + /* glColor3ub sets RGB and forces alpha to 1.0. */ + void test_color3ub_sets_rgb_and_forces_alpha_to_one() { + glColor3ub(255, 128, 64); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 255.0f / 255.0f, 0.005f); + assert_close(color[G8IDX], 128.0f / 255.0f, 0.005f); + assert_close(color[B8IDX], 64.0f / 255.0f, 0.005f); + assert_close(color[A8IDX], 1.0f, 0.001f); + } + + /* glColor4fv reads RGBA from array. */ + void test_color4fv_sets_from_array() { + GLfloat c[4] = { 0.1f, 0.2f, 0.3f, 0.4f }; + glColor4fv(c); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 0.1f, 0.001f); + assert_close(color[G8IDX], 0.2f, 0.001f); + assert_close(color[B8IDX], 0.3f, 0.001f); + assert_close(color[A8IDX], 0.4f, 0.001f); + } + + /* glColor3fv reads RGB from array and forces alpha to 1. */ + void test_color3fv_sets_from_array_and_forces_alpha_to_one() { + GLfloat c[3] = { 0.5f, 0.6f, 0.7f }; + glColor3fv(c); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 0.5f, 0.001f); + assert_close(color[G8IDX], 0.6f, 0.001f); + assert_close(color[B8IDX], 0.7f, 0.001f); + assert_close(color[A8IDX], 1.0f, 0.001f); + } + + /* glColor4ubv reads RGBA bytes from array. */ + void test_color4ubv_sets_from_array() { + GLubyte c[4] = { 200, 100, 50, 150 }; + glColor4ubv(c); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 200.0f / 255.0f, 0.005f); + assert_close(color[G8IDX], 100.0f / 255.0f, 0.005f); + assert_close(color[B8IDX], 50.0f / 255.0f, 0.005f); + assert_close(color[A8IDX], 150.0f / 255.0f, 0.005f); + } + + /* glColor3ubv reads RGB bytes and forces alpha to 1. */ + void test_color3ubv_sets_from_array_and_forces_alpha_to_one() { + GLubyte c[3] = { 255, 0, 128 }; + glColor3ubv(c); + float* color = _glCurrentColor(); + assert_close(color[R8IDX], 1.0f, 0.001f); + assert_close(color[G8IDX], 0.0f, 0.001f); + assert_close(color[B8IDX], 128.0f / 255.0f, 0.005f); + assert_close(color[A8IDX], 1.0f, 0.001f); + } + + /* Valid colour calls must not produce a GL error. */ + void test_color_calls_produce_no_gl_error() { + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + glColor3f(0.5f, 0.5f, 0.5f); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + glColor4ub(128, 128, 128, 255); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + glColor3ub(200, 100, 50); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + } +}; + + +/* ========================================================================= + * GlColorPointerTests + * + * Tests for glColorPointer. Verifies that the AttribPointer for colour + * (ATTRIB_LIST.colour) stores the correct size, type, stride and data + * pointer, that stride=0 triggers the automatic stride calculation, and + * that invalid size arguments raise GL_INVALID_VALUE. + * =========================================================================*/ +class GlColorPointerTests : public test::TestCase { +public: + void set_up() { + GLdcConfig config; + glKosInitConfig(&config); + config.texture_twiddle = false; + glKosInitEx(&config); + /* Reset enabled flags – _glInitAttributePointers does not do this. */ + ATTRIB_LIST.enabled = 0; + } + + void tear_down() { + glKosShutdown(); + } + + /* size=4, GL_FLOAT: stride auto-calculated to 4*4=16. */ + void test_colorpointer_size4_float_stores_metadata() { + GLfloat colors[12] = {}; + glColorPointer(4, GL_FLOAT, 0, colors); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + assert_equal(ATTRIB_LIST.colour.size, 4); + assert_equal(ATTRIB_LIST.colour.type, (GLenum)GL_FLOAT); + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)(4 * sizeof(GLfloat))); + assert_equal(ATTRIB_LIST.colour.ptr, (const void*)colors); + } + + /* size=3, GL_FLOAT: stride auto-calculated to 3*4=12. */ + void test_colorpointer_size3_float_stores_metadata() { + GLfloat colors[9] = {}; + glColorPointer(3, GL_FLOAT, 0, colors); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + assert_equal(ATTRIB_LIST.colour.size, 3); + assert_equal(ATTRIB_LIST.colour.type, (GLenum)GL_FLOAT); + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)(3 * sizeof(GLfloat))); + } + + /* size=4, GL_UNSIGNED_BYTE: stride auto-calculated to 4*1=4. */ + void test_colorpointer_size4_ubyte_stores_metadata() { + GLubyte colors[12] = {}; + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + assert_equal(ATTRIB_LIST.colour.size, 4); + assert_equal(ATTRIB_LIST.colour.type, (GLenum)GL_UNSIGNED_BYTE); + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)(4 * sizeof(GLubyte))); + } + + /* size=3, GL_UNSIGNED_BYTE: stride auto-calculated to 3*1=3. */ + void test_colorpointer_size3_ubyte_stores_metadata() { + GLubyte colors[9] = {}; + glColorPointer(3, GL_UNSIGNED_BYTE, 0, colors); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + assert_equal(ATTRIB_LIST.colour.size, 3); + assert_equal(ATTRIB_LIST.colour.type, (GLenum)GL_UNSIGNED_BYTE); + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)(3 * sizeof(GLubyte))); + } + + /* GL_BGRA is a valid size token; stride becomes 4 bytes for UNSIGNED_BYTE. */ + void test_colorpointer_bgra_ubyte_is_valid() { + GLubyte colors[12] = {}; + glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, 0, colors); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + assert_equal(ATTRIB_LIST.colour.size, (GLint)GL_BGRA); + assert_equal(ATTRIB_LIST.colour.type, (GLenum)GL_UNSIGNED_BYTE); + /* 4 bytes per colour element */ + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)4); + } + + /* size=1 is not a legal value; GL_INVALID_VALUE must be raised. */ + void test_colorpointer_size1_raises_invalid_value() { + GLfloat colors[4] = {}; + glColorPointer(1, GL_FLOAT, 0, colors); + assert_equal(glGetError(), (GLenum)GL_INVALID_VALUE); + } + + /* size=2 is not a legal value; GL_INVALID_VALUE must be raised. */ + void test_colorpointer_size2_raises_invalid_value() { + GLfloat colors[8] = {}; + glColorPointer(2, GL_FLOAT, 0, colors); + assert_equal(glGetError(), (GLenum)GL_INVALID_VALUE); + } + + /* Automatic stride for float4 = 16 bytes. */ + void test_colorpointer_auto_stride_float4() { + GLfloat colors[12] = {}; + glColorPointer(4, GL_FLOAT, 0, colors); + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)(4 * sizeof(GLfloat))); + } + + /* Automatic stride for float3 = 12 bytes. */ + void test_colorpointer_auto_stride_float3() { + GLfloat colors[9] = {}; + glColorPointer(3, GL_FLOAT, 0, colors); + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)(3 * sizeof(GLfloat))); + } + + /* Automatic stride for ubyte4 = 4 bytes. */ + void test_colorpointer_auto_stride_ubyte4() { + GLubyte colors[12] = {}; + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)(4 * sizeof(GLubyte))); + } + + /* An explicit non-zero stride must be kept unchanged. */ + void test_colorpointer_explicit_stride_is_preserved() { + GLfloat colors[12] = {}; + glColorPointer(4, GL_FLOAT, 32, colors); + assert_equal(ATTRIB_LIST.colour.stride, (GLsizei)32); + } + + /* The data pointer must be stored verbatim. */ + void test_colorpointer_stores_pointer() { + GLfloat colors[12] = {}; + glColorPointer(4, GL_FLOAT, 0, colors); + assert_equal(ATTRIB_LIST.colour.ptr, (const void*)colors); + } + + /* A NULL pointer is accepted without raising an error. */ + void test_colorpointer_null_pointer_is_accepted() { + glColorPointer(4, GL_FLOAT, 0, NULL); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + } + + /* Calling glColorPointer must mark the colour attribute as dirty. + * Uses size=3 to guarantee a state change from the size=4 default + * initialised by _glInitAttributePointers, avoiding the + * _glStateUnchanged early-return path. */ + void test_colorpointer_marks_colour_attribute_dirty() { + GLfloat colors[9] = {}; + ATTRIB_LIST.dirty = 0; + glColorPointer(3, GL_FLOAT, 0, colors); + assert_true(ATTRIB_LIST.dirty & COLOR_ENABLED_FLAG); + } +}; + + +/* ========================================================================= + * GlSecondaryColorPointerTests + * + * Mirrors GlColorPointerTests for the secondary (offset) colour pointer + * stored in ATTRIB_LIST.s_color. + * =========================================================================*/ +class GlSecondaryColorPointerTests : public test::TestCase { +public: + void set_up() { + GLdcConfig config; + glKosInitConfig(&config); + config.texture_twiddle = false; + glKosInitEx(&config); + ATTRIB_LIST.enabled = 0; + } + + void tear_down() { + glKosShutdown(); + } + + /* size=4, GL_FLOAT: basic happy path. */ + void test_secondary_colorpointer_size4_float_stores_metadata() { + GLfloat colors[12] = {}; + glSecondaryColorPointer(4, GL_FLOAT, 0, colors); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + assert_equal(ATTRIB_LIST.s_color.size, 4); + assert_equal(ATTRIB_LIST.s_color.type, (GLenum)GL_FLOAT); + assert_equal(ATTRIB_LIST.s_color.stride, (GLsizei)(4 * sizeof(GLfloat))); + assert_equal(ATTRIB_LIST.s_color.ptr, (const void*)colors); + } + + /* size=3, GL_FLOAT: three-component secondary colour. */ + void test_secondary_colorpointer_size3_float_stores_metadata() { + GLfloat colors[9] = {}; + glSecondaryColorPointer(3, GL_FLOAT, 0, colors); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + assert_equal(ATTRIB_LIST.s_color.size, 3); + assert_equal(ATTRIB_LIST.s_color.type, (GLenum)GL_FLOAT); + assert_equal(ATTRIB_LIST.s_color.stride, (GLsizei)(3 * sizeof(GLfloat))); + } + + /* GL_BGRA is a valid size for secondary colour. */ + void test_secondary_colorpointer_bgra_ubyte_is_valid() { + GLubyte colors[12] = {}; + glSecondaryColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, 0, colors); + assert_equal(glGetError(), (GLenum)GL_NO_ERROR); + assert_equal(ATTRIB_LIST.s_color.size, (GLint)GL_BGRA); + assert_equal(ATTRIB_LIST.s_color.type, (GLenum)GL_UNSIGNED_BYTE); + assert_equal(ATTRIB_LIST.s_color.stride, (GLsizei)4); + } + + /* size=1 must raise GL_INVALID_VALUE. */ + void test_secondary_colorpointer_size1_raises_invalid_value() { + GLfloat colors[4] = {}; + glSecondaryColorPointer(1, GL_FLOAT, 0, colors); + assert_equal(glGetError(), (GLenum)GL_INVALID_VALUE); + } + + /* size=2 must raise GL_INVALID_VALUE. */ + void test_secondary_colorpointer_size2_raises_invalid_value() { + GLfloat colors[8] = {}; + glSecondaryColorPointer(2, GL_FLOAT, 0, colors); + assert_equal(glGetError(), (GLenum)GL_INVALID_VALUE); + } + + /* Automatic stride for float4. */ + void test_secondary_colorpointer_auto_stride_float4() { + GLfloat colors[12] = {}; + glSecondaryColorPointer(4, GL_FLOAT, 0, colors); + assert_equal(ATTRIB_LIST.s_color.stride, (GLsizei)(4 * sizeof(GLfloat))); + } + + /* Automatic stride for ubyte3. */ + void test_secondary_colorpointer_auto_stride_ubyte3() { + GLubyte colors[9] = {}; + glSecondaryColorPointer(3, GL_UNSIGNED_BYTE, 0, colors); + assert_equal(ATTRIB_LIST.s_color.stride, (GLsizei)(3 * sizeof(GLubyte))); + } + + /* An explicit stride must be preserved. */ + void test_secondary_colorpointer_explicit_stride_is_preserved() { + GLfloat colors[12] = {}; + glSecondaryColorPointer(4, GL_FLOAT, 24, colors); + assert_equal(ATTRIB_LIST.s_color.stride, (GLsizei)24); + } + + /* Data pointer must be stored verbatim. */ + void test_secondary_colorpointer_stores_pointer() { + GLfloat colors[12] = {}; + glSecondaryColorPointer(4, GL_FLOAT, 0, colors); + assert_equal(ATTRIB_LIST.s_color.ptr, (const void*)colors); + } + + /* Calling glSecondaryColorPointer must dirty the s_color attribute. + * _glInitAttributePointers does not touch s_color, so its state + * persists across tests. Zeroing it here guarantees a state + * change so _glStateUnchanged does not short-circuit the call. */ + void test_secondary_colorpointer_marks_attribute_dirty() { + memset(&ATTRIB_LIST.s_color, 0, sizeof(ATTRIB_LIST.s_color)); + GLfloat colors[12] = {}; + ATTRIB_LIST.dirty = 0; + glSecondaryColorPointer(4, GL_FLOAT, 0, colors); + assert_true(ATTRIB_LIST.dirty & S_COLOR_ENABLED_FLAG); + } +}; + + +/* ========================================================================= + * GlClientStateTests + * + * Tests for glEnableClientState / glDisableClientState. Verifies that the + * appropriate flag bits in ATTRIB_LIST.enabled are set or cleared, that the + * dirty flag is updated, and that invalid enumerants raise GL_INVALID_ENUM. + * =========================================================================*/ +class GlClientStateTests : public test::TestCase { +public: + void set_up() { + GLdcConfig config; + glKosInitConfig(&config); + config.texture_twiddle = false; + glKosInitEx(&config); + /* Clear all enabled flags for a clean baseline. */ + ATTRIB_LIST.enabled = 0; + } + + void tear_down() { + glKosShutdown(); + } + + /* Enabling GL_COLOR_ARRAY must set COLOR_ENABLED_FLAG. */ + void test_enable_color_array_sets_flag() { + glEnableClientState(GL_COLOR_ARRAY); + assert_true(ATTRIB_LIST.enabled & COLOR_ENABLED_FLAG); + } + + /* Disabling GL_COLOR_ARRAY must clear COLOR_ENABLED_FLAG. */ + void test_disable_color_array_clears_flag() { + glEnableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + assert_false(ATTRIB_LIST.enabled & COLOR_ENABLED_FLAG); + } + + /* Enabling GL_VERTEX_ARRAY must set VERTEX_ENABLED_FLAG. */ + void test_enable_vertex_array_sets_flag() { + glEnableClientState(GL_VERTEX_ARRAY); + assert_true(ATTRIB_LIST.enabled & VERTEX_ENABLED_FLAG); + } + + /* Disabling GL_VERTEX_ARRAY must clear VERTEX_ENABLED_FLAG. */ + void test_disable_vertex_array_clears_flag() { + glEnableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + assert_false(ATTRIB_LIST.enabled & VERTEX_ENABLED_FLAG); + } + + /* Enabling GL_NORMAL_ARRAY must set NORMAL_ENABLED_FLAG. */ + void test_enable_normal_array_sets_flag() { + glEnableClientState(GL_NORMAL_ARRAY); + assert_true(ATTRIB_LIST.enabled & NORMAL_ENABLED_FLAG); + } + + /* Disabling GL_NORMAL_ARRAY must clear NORMAL_ENABLED_FLAG. */ + void test_disable_normal_array_clears_flag() { + glEnableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + assert_false(ATTRIB_LIST.enabled & NORMAL_ENABLED_FLAG); + } + + /* With client texture unit 0, GL_TEXTURE_COORD_ARRAY sets UV_ENABLED_FLAG. */ + void test_enable_texture_coord_array_sets_uv_flag() { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + assert_true(ATTRIB_LIST.enabled & UV_ENABLED_FLAG); + } + + /* Disabling GL_TEXTURE_COORD_ARRAY clears UV_ENABLED_FLAG. */ + void test_disable_texture_coord_array_clears_uv_flag() { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + assert_false(ATTRIB_LIST.enabled & UV_ENABLED_FLAG); + } + + /* Multiple arrays can be enabled independently. */ + void test_multiple_arrays_enabled_simultaneously() { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + assert_true(ATTRIB_LIST.enabled & VERTEX_ENABLED_FLAG); + assert_true(ATTRIB_LIST.enabled & COLOR_ENABLED_FLAG); + assert_true(ATTRIB_LIST.enabled & NORMAL_ENABLED_FLAG); + } + + /* Disabling one array must not affect others. */ + void test_disable_one_array_does_not_affect_others() { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + assert_true (ATTRIB_LIST.enabled & VERTEX_ENABLED_FLAG); + assert_false(ATTRIB_LIST.enabled & COLOR_ENABLED_FLAG); + } + + /* An invalid cap must raise GL_INVALID_ENUM. */ + void test_enable_invalid_cap_raises_invalid_enum() { + glEnableClientState(0xDEAD); + assert_equal(glGetError(), (GLenum)GL_INVALID_ENUM); + } + + /* An invalid cap on disable must raise GL_INVALID_ENUM. */ + void test_disable_invalid_cap_raises_invalid_enum() { + glDisableClientState(0xDEAD); + assert_equal(glGetError(), (GLenum)GL_INVALID_ENUM); + } + + /* Enabling an array must mark the corresponding dirty bit. */ + void test_enable_color_array_marks_dirty() { + ATTRIB_LIST.dirty = 0; + glEnableClientState(GL_COLOR_ARRAY); + assert_true(ATTRIB_LIST.dirty & COLOR_ENABLED_FLAG); + } + + /* Disabling an array must also mark the dirty bit so that the function + * pointer is recomputed on the next draw. */ + void test_disable_color_array_marks_dirty() { + glEnableClientState(GL_COLOR_ARRAY); + ATTRIB_LIST.dirty = 0; + glDisableClientState(GL_COLOR_ARRAY); + assert_true(ATTRIB_LIST.dirty & COLOR_ENABLED_FLAG); + } +}; diff --git a/tests/test_nearz_clipping.h b/tests/test_nearz_clipping.h new file mode 100644 index 0000000..889bc43 --- /dev/null +++ b/tests/test_nearz_clipping.h @@ -0,0 +1,11 @@ +#include "tools/test.h" + + + +class ZClipTests : public test::TestCase { +public: + + void test_case_000() { + + } +}; \ No newline at end of file diff --git a/tests/test_pvr_vertex_submission.h b/tests/test_pvr_vertex_submission.h new file mode 100644 index 0000000..aea9b42 --- /dev/null +++ b/tests/test_pvr_vertex_submission.h @@ -0,0 +1,522 @@ +#pragma once + +#include "tools/test.h" + +#include +#include +#include + +/* + * Internal headers needed to inspect the Tile Accelerator (TA) submission + * buffers directly after a draw call. + * + * After glDrawArrays / glEnd, the GL driver fills one of three poly-lists + * (OP_LIST / PT_LIST / TR_LIST). Each list is an AlignedVector whose + * elements are 64-byte Vertex structs. The first element in the vector + * after a draw is a PolyHeader (also 64 bytes), followed by the actual + * vertex data. + * + * This file tests: + * - That the expected number of PolyHeader + Vertex entries appear + * in OP_LIST after various draw calls. + * - That the GPU_CMD_VERTEX / GPU_CMD_VERTEX_EOL flags are set on + * the correct vertices. + * - That the PolyHeader command word is valid. + * - That primary colour set via glColor* or glColorPointer appears + * correctly in each submitted vertex's argb[] field. + */ +#include "GL/private.h" +#include "containers/aligned_vector.h" + + +/* ========================================================================= + * PVRVertexSubmissionTests + * =========================================================================*/ +class PVRVertexSubmissionTests : public test::TestCase { +public: + void set_up() { + GLdcConfig config; + glKosInitConfig(&config); + config.texture_twiddle = false; + glKosInitEx(&config); + + /* Explicit clean slate for colour and client-state. */ + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + ATTRIB_LIST.enabled = 0; + } + + void tear_down() { + glKosShutdown(); + } + + /* Convenience: cast element i of a list to Vertex*. */ + static Vertex* vertex_at(PolyList* list, uint32_t i) { + return (Vertex*) aligned_vector_at(&list->vector, i); + } + + /* ----------------------------------------------------------------------- + * Basic list structure + * -------------------------------------------------------------------- */ + + /* OP_LIST must be empty before the first draw call. */ + void test_op_list_is_empty_before_any_draw() { + assert_equal(aligned_vector_size(&OP_LIST.vector), 0u); + } + + /* Drawing a single triangle via immediate mode must produce exactly + * one PolyHeader and three vertices (four elements total). */ + void test_immediate_triangle_produces_four_list_entries() { + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + assert_equal(aligned_vector_size(&OP_LIST.vector), 4u); + } + + /* A second triangle drawn while GPU state is unchanged must not add + * another PolyHeader, yielding 4 + 3 = 7 entries. */ + void test_second_triangle_without_state_change_shares_header() { + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + glBegin(GL_TRIANGLES); + glVertex3f(-0.5f, -0.5f, 0.5f); + glVertex3f( 0.5f, -0.5f, 0.5f); + glVertex3f( 0.0f, 0.5f, 0.5f); + glEnd(); + + /* 1 header + 3 verts + 3 verts = 7 */ + assert_equal(aligned_vector_size(&OP_LIST.vector), 7u); + } + + /* After glKosSwapBuffers the list must be cleared. */ + void test_op_list_is_empty_after_swap_buffers() { + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + glKosSwapBuffers(); + + assert_equal(aligned_vector_size(&OP_LIST.vector), 0u); + } + + /* ----------------------------------------------------------------------- + * PolyHeader validation + * -------------------------------------------------------------------- */ + + /* The PolyHeader command word must have the polygon-header type bits + * set (upper byte = 0x80) as defined by GPU_CMD_POLYHDR = 0x80840000. */ + void test_poly_header_has_correct_upper_byte() { + glBegin(GL_TRIANGLES); + glVertex3f(0.0f, 0.0f, 0.5f); + glVertex3f(1.0f, 0.0f, 0.5f); + glVertex3f(0.0f, 1.0f, 0.5f); + glEnd(); + + PolyHeader* hdr = (PolyHeader*) aligned_vector_at(&OP_LIST.vector, 0); + /* Top byte must be 0x80 (polygon-header command). */ + assert_equal(hdr->cmd & 0xFF000000u, 0x80000000u); + } + + /* For the opaque list the blend word in mode2 must encode ONE/ZERO. */ + void test_opaque_poly_header_blend_is_one_zero() { + glBegin(GL_TRIANGLES); + glVertex3f(0.0f, 0.0f, 0.5f); + glVertex3f(1.0f, 0.0f, 0.5f); + glVertex3f(0.0f, 1.0f, 0.5f); + glEnd(); + + PolyHeader* hdr = (PolyHeader*) aligned_vector_at(&OP_LIST.vector, 0); + + /* src blend = GPU_BLEND_ONE (1) in bits 31-29 of mode2 */ + uint32_t src = (hdr->mode2 >> GPU_TA_PM2_SRCBLEND_SHIFT) & 0x7; + /* dst blend = GPU_BLEND_ZERO (0) in bits 28-26 of mode2 */ + uint32_t dst = (hdr->mode2 >> GPU_TA_PM2_DSTBLEND_SHIFT) & 0x7; + + assert_equal(src, (uint32_t)GPU_BLEND_ONE); + assert_equal(dst, (uint32_t)GPU_BLEND_ZERO); + } + + /* ----------------------------------------------------------------------- + * Vertex flag tests + * -------------------------------------------------------------------- */ + + /* For GL_TRIANGLES, vertices 0 and 1 of the strip carry GPU_CMD_VERTEX; + * vertex 2 (end of triangle) carries GPU_CMD_VERTEX_EOL. */ + void test_triangle_vertex_flags_are_set_correctly() { + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + /* Index 0 is the PolyHeader; vertices start at index 1. */ + Vertex* v0 = vertex_at(&OP_LIST, 1); + Vertex* v1 = vertex_at(&OP_LIST, 2); + Vertex* v2 = vertex_at(&OP_LIST, 3); + + assert_equal(v0->flags, (uint32_t)GPU_CMD_VERTEX); + assert_equal(v1->flags, (uint32_t)GPU_CMD_VERTEX); + assert_equal(v2->flags, (uint32_t)GPU_CMD_VERTEX_EOL); + } + + /* For two back-to-back GL_TRIANGLES the EOL flag must appear on every + * third vertex (indices 3 and 6 within the combined list). */ + void test_two_triangles_have_eol_on_every_third_vertex() { + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + glBegin(GL_TRIANGLES); + glVertex3f(-0.5f, -0.5f, 0.5f); + glVertex3f( 0.5f, -0.5f, 0.5f); + glVertex3f( 0.0f, 0.5f, 0.5f); + glEnd(); + + /* 1 header + 6 vertices (indices 1..6) */ + assert_equal(vertex_at(&OP_LIST, 1)->flags, (uint32_t)GPU_CMD_VERTEX); + assert_equal(vertex_at(&OP_LIST, 2)->flags, (uint32_t)GPU_CMD_VERTEX); + assert_equal(vertex_at(&OP_LIST, 3)->flags, (uint32_t)GPU_CMD_VERTEX_EOL); + assert_equal(vertex_at(&OP_LIST, 4)->flags, (uint32_t)GPU_CMD_VERTEX); + assert_equal(vertex_at(&OP_LIST, 5)->flags, (uint32_t)GPU_CMD_VERTEX); + assert_equal(vertex_at(&OP_LIST, 6)->flags, (uint32_t)GPU_CMD_VERTEX_EOL); + } + + /* ----------------------------------------------------------------------- + * Immediate-mode colour tests + * -------------------------------------------------------------------- */ + + /* A single colour set before all vertices must appear identically in + * every submitted vertex (alpha, red, green, blue in ARGB order). */ + void test_immediate_single_color_appears_in_all_vertices() { + /* Set a distinctive colour: R=1, G=0, B=0, A=1 */ + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); + + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + for(uint32_t i = 1; i <= 3; ++i) { + Vertex* v = vertex_at(&OP_LIST, i); + assert_close(v->argb[A8IDX], 1.0f, 0.001f); + assert_close(v->argb[R8IDX], 1.0f, 0.001f); + assert_close(v->argb[G8IDX], 0.0f, 0.001f); + assert_close(v->argb[B8IDX], 0.0f, 0.001f); + } + } + + /* glColor4ub must also produce the correct normalised float values in + * the submitted vertices. */ + void test_immediate_color4ub_appears_correctly_in_vertex() { + glColor4ub(0, 255, 0, 255); /* green */ + + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + Vertex* v = vertex_at(&OP_LIST, 1); + assert_close(v->argb[A8IDX], 1.0f, 0.005f); + assert_close(v->argb[R8IDX], 0.0f, 0.005f); + assert_close(v->argb[G8IDX], 1.0f, 0.005f); + assert_close(v->argb[B8IDX], 0.0f, 0.005f); + } + + /* Changing the colour between vertex calls must produce per-vertex + * colour variation in the submitted list. */ + void test_immediate_per_vertex_colors_are_distinct() { + glBegin(GL_TRIANGLES); + glColor4f(1.0f, 0.0f, 0.0f, 1.0f); /* red */ + glVertex3f(-1.0f, -1.0f, 0.5f); + + glColor4f(0.0f, 1.0f, 0.0f, 1.0f); /* green */ + glVertex3f( 1.0f, -1.0f, 0.5f); + + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); /* blue */ + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + Vertex* v0 = vertex_at(&OP_LIST, 1); /* red */ + Vertex* v1 = vertex_at(&OP_LIST, 2); /* green */ + Vertex* v2 = vertex_at(&OP_LIST, 3); /* blue */ + + /* v0 = red */ + assert_close(v0->argb[R8IDX], 1.0f, 0.001f); + assert_close(v0->argb[G8IDX], 0.0f, 0.001f); + assert_close(v0->argb[B8IDX], 0.0f, 0.001f); + assert_close(v0->argb[A8IDX], 1.0f, 0.001f); + + /* v1 = green */ + assert_close(v1->argb[R8IDX], 0.0f, 0.001f); + assert_close(v1->argb[G8IDX], 1.0f, 0.001f); + assert_close(v1->argb[B8IDX], 0.0f, 0.001f); + assert_close(v1->argb[A8IDX], 1.0f, 0.001f); + + /* v2 = blue */ + assert_close(v2->argb[R8IDX], 0.0f, 0.001f); + assert_close(v2->argb[G8IDX], 0.0f, 0.001f); + assert_close(v2->argb[B8IDX], 1.0f, 0.001f); + assert_close(v2->argb[A8IDX], 1.0f, 0.001f); + } + + /* glColor3f (no alpha) must result in alpha=1 in the submitted vertex. */ + void test_immediate_color3f_vertex_has_full_alpha() { + glColor3f(0.5f, 0.25f, 0.1f); + + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + Vertex* v = vertex_at(&OP_LIST, 1); + assert_close(v->argb[A8IDX], 1.0f, 0.001f); + assert_close(v->argb[R8IDX], 0.5f, 0.001f); + assert_close(v->argb[G8IDX], 0.25f, 0.001f); + assert_close(v->argb[B8IDX], 0.1f, 0.001f); + } + + /* ----------------------------------------------------------------------- + * Vertex-array colour tests (glColorPointer path) + * -------------------------------------------------------------------- */ + + /* When no colour array is active the current colour (set via glColor*) + * must be used for every submitted vertex. */ + void test_no_color_pointer_uses_current_color_for_all_vertices() { + glColor4f(0.5f, 0.25f, 0.1f, 0.8f); + + GLfloat verts[] = { + -1.0f, -1.0f, 0.5f, + 1.0f, -1.0f, 0.5f, + 0.0f, 1.0f, 0.5f + }; + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, verts); + glDrawArrays(GL_TRIANGLES, 0, 3); + glDisableClientState(GL_VERTEX_ARRAY); + + Vertex* v = vertex_at(&OP_LIST, 1); + assert_close(v->argb[R8IDX], 0.5f, 0.001f); + assert_close(v->argb[G8IDX], 0.25f, 0.001f); + assert_close(v->argb[B8IDX], 0.1f, 0.001f); + assert_close(v->argb[A8IDX], 0.8f, 0.001f); + } + + /* Colours supplied via glColorPointer (GL_FLOAT, size 4, RGBA order) + * must reach the submitted vertices in ARGB layout. + * + * _readColour4fARGB treats the input as RGBA and writes: + * argb[R8IDX] = input[0] argb[G8IDX] = input[1] + * argb[B8IDX] = input[2] argb[A8IDX] = input[3] + */ + void test_color_pointer_float4_rgba_stored_correctly() { + GLfloat verts[] = { + -1.0f, -1.0f, 0.5f, + 1.0f, -1.0f, 0.5f, + 0.0f, 1.0f, 0.5f + }; + /* RGBA float colours: red, green, blue */ + GLfloat colors[] = { + 1.0f, 0.0f, 0.0f, 1.0f, /* red */ + 0.0f, 1.0f, 0.0f, 1.0f, /* green */ + 0.0f, 0.0f, 1.0f, 1.0f /* blue */ + }; + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, verts); + glColorPointer(4, GL_FLOAT, 0, colors); + glDrawArrays(GL_TRIANGLES, 0, 3); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + Vertex* v0 = vertex_at(&OP_LIST, 1); /* red */ + Vertex* v1 = vertex_at(&OP_LIST, 2); /* green */ + Vertex* v2 = vertex_at(&OP_LIST, 3); /* blue */ + + /* v0 = red */ + assert_close(v0->argb[R8IDX], 1.0f, 0.001f); + assert_close(v0->argb[G8IDX], 0.0f, 0.001f); + assert_close(v0->argb[B8IDX], 0.0f, 0.001f); + assert_close(v0->argb[A8IDX], 1.0f, 0.001f); + + /* v1 = green */ + assert_close(v1->argb[R8IDX], 0.0f, 0.001f); + assert_close(v1->argb[G8IDX], 1.0f, 0.001f); + assert_close(v1->argb[B8IDX], 0.0f, 0.001f); + assert_close(v1->argb[A8IDX], 1.0f, 0.001f); + + /* v2 = blue */ + assert_close(v2->argb[R8IDX], 0.0f, 0.001f); + assert_close(v2->argb[G8IDX], 0.0f, 0.001f); + assert_close(v2->argb[B8IDX], 1.0f, 0.001f); + assert_close(v2->argb[A8IDX], 1.0f, 0.001f); + } + + /* Same test as above but using GL_UNSIGNED_BYTE colours (RGBA order). */ + void test_color_pointer_ubyte4_rgba_stored_correctly() { + GLfloat verts[] = { + -1.0f, -1.0f, 0.5f, + 1.0f, -1.0f, 0.5f, + 0.0f, 1.0f, 0.5f + }; + /* RGBA ubyte colours: red, green, blue */ + GLubyte colors[] = { + 255, 0, 0, 255, /* red */ + 0, 255, 0, 255, /* green */ + 0, 0, 255, 255 /* blue */ + }; + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, verts); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); + glDrawArrays(GL_TRIANGLES, 0, 3); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + Vertex* v0 = vertex_at(&OP_LIST, 1); + Vertex* v1 = vertex_at(&OP_LIST, 2); + Vertex* v2 = vertex_at(&OP_LIST, 3); + + /* v0 = red */ + assert_close(v0->argb[R8IDX], 1.0f, 0.005f); + assert_close(v0->argb[G8IDX], 0.0f, 0.005f); + assert_close(v0->argb[B8IDX], 0.0f, 0.005f); + assert_close(v0->argb[A8IDX], 1.0f, 0.005f); + + /* v1 = green */ + assert_close(v1->argb[R8IDX], 0.0f, 0.005f); + assert_close(v1->argb[G8IDX], 1.0f, 0.005f); + assert_close(v1->argb[B8IDX], 0.0f, 0.005f); + assert_close(v1->argb[A8IDX], 1.0f, 0.005f); + + /* v2 = blue */ + assert_close(v2->argb[R8IDX], 0.0f, 0.005f); + assert_close(v2->argb[G8IDX], 0.0f, 0.005f); + assert_close(v2->argb[B8IDX], 1.0f, 0.005f); + assert_close(v2->argb[A8IDX], 1.0f, 0.005f); + } + + /* Three-component float colour pointer (GL_FLOAT, size 3). + * _readColour3fARGB reads RGB and forces alpha to 1.0. */ + void test_color_pointer_float3_rgb_forces_alpha_to_one() { + GLfloat verts[] = { + -1.0f, -1.0f, 0.5f, + 1.0f, -1.0f, 0.5f, + 0.0f, 1.0f, 0.5f + }; + /* RGB float colours only (no alpha channel) */ + GLfloat colors[] = { + 0.5f, 0.0f, 0.0f, /* dark red */ + 0.0f, 0.5f, 0.0f, /* dark green */ + 0.0f, 0.0f, 0.5f /* dark blue */ + }; + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, verts); + glColorPointer(3, GL_FLOAT, 0, colors); + glDrawArrays(GL_TRIANGLES, 0, 3); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + Vertex* v0 = vertex_at(&OP_LIST, 1); + /* Alpha must be 1 even though input has no alpha channel. */ + assert_close(v0->argb[A8IDX], 1.0f, 0.001f); + assert_close(v0->argb[R8IDX], 0.5f, 0.001f); + assert_close(v0->argb[G8IDX], 0.0f, 0.001f); + assert_close(v0->argb[B8IDX], 0.0f, 0.001f); + + Vertex* v1 = vertex_at(&OP_LIST, 2); + assert_close(v1->argb[A8IDX], 1.0f, 0.001f); + assert_close(v1->argb[R8IDX], 0.0f, 0.001f); + assert_close(v1->argb[G8IDX], 0.5f, 0.001f); + assert_close(v1->argb[B8IDX], 0.0f, 0.001f); + } + + /* Using glDrawElements with an index array must produce the same vertex + * colours as the equivalent glDrawArrays call. */ + void test_draw_elements_colour_matches_draw_arrays() { + GLfloat verts[] = { + -1.0f, -1.0f, 0.5f, /* 0 */ + 1.0f, -1.0f, 0.5f, /* 1 */ + 0.0f, 1.0f, 0.5f /* 2 */ + }; + GLfloat colors[] = { + 0.2f, 0.4f, 0.6f, 1.0f, /* 0 */ + 0.3f, 0.5f, 0.7f, 1.0f, /* 1 */ + 0.4f, 0.6f, 0.8f, 1.0f /* 2 */ + }; + GLubyte indices[] = { 0, 1, 2 }; + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, verts); + glColorPointer(4, GL_FLOAT, 0, colors); + glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, indices); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + Vertex* v0 = vertex_at(&OP_LIST, 1); + assert_close(v0->argb[R8IDX], 0.2f, 0.001f); + assert_close(v0->argb[G8IDX], 0.4f, 0.001f); + assert_close(v0->argb[B8IDX], 0.6f, 0.001f); + assert_close(v0->argb[A8IDX], 1.0f, 0.001f); + } + + /* Blending enabled must route geometry to TR_LIST, not OP_LIST. */ + void test_blended_triangle_goes_to_tr_list() { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBegin(GL_TRIANGLES); + glColor4f(1.0f, 0.0f, 0.0f, 0.5f); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + /* Opaque list must remain empty. */ + assert_equal(aligned_vector_size(&OP_LIST.vector), 0u); + /* Transparent list must have 1 header + 3 vertices. */ + assert_equal(aligned_vector_size(&TR_LIST.vector), 4u); + + glDisable(GL_BLEND); + } + + /* Colours in the TR_LIST must also reflect what was submitted. */ + void test_blended_triangle_colour_in_tr_list() { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glColor4f(0.0f, 1.0f, 0.0f, 0.5f); /* semi-transparent green */ + + glBegin(GL_TRIANGLES); + glVertex3f(-1.0f, -1.0f, 0.5f); + glVertex3f( 1.0f, -1.0f, 0.5f); + glVertex3f( 0.0f, 1.0f, 0.5f); + glEnd(); + + Vertex* v = (Vertex*) aligned_vector_at(&TR_LIST.vector, 1); + assert_close(v->argb[R8IDX], 0.0f, 0.001f); + assert_close(v->argb[G8IDX], 1.0f, 0.001f); + assert_close(v->argb[B8IDX], 0.0f, 0.001f); + assert_close(v->argb[A8IDX], 0.5f, 0.001f); + + glDisable(GL_BLEND); + } +}; diff --git a/tools/test.h b/tools/test.h index b4ef409..7f1ad89 100644 --- a/tools/test.h +++ b/tools/test.h @@ -26,6 +26,7 @@ #include #include #include +#include #define assert_equal(expected, actual) _assert_equal((expected), (actual), __FILE__, __LINE__) #define assert_not_equal(expected, actual) _assert_not_equal((expected), (actual), __FILE__, __LINE__)