diff --git a/GL/flush.c b/GL/flush.c index 5122840..f17a9c5 100644 --- a/GL/flush.c +++ b/GL/flush.c @@ -9,6 +9,8 @@ #include "profiler.h" #include "version.h" +#include "flush.h" + #define TA_SQ_ADDR (unsigned int *)(void *) \ (0xe0000000 | (((unsigned long)0x10000000) & 0x03ffffe0)) @@ -18,13 +20,6 @@ static PolyList TR_LIST; static const int STRIDE = sizeof(Vertex) / sizeof(GLuint); -typedef struct { - int count; - Vertex* current; - GLboolean current_is_vertex; -} ListIterator; - - GL_FORCE_INLINE GLboolean isVertex(const Vertex* vertex) { return ( vertex->flags == PVR_CMD_VERTEX || @@ -33,46 +28,101 @@ GL_FORCE_INLINE GLboolean isVertex(const Vertex* vertex) { } GL_FORCE_INLINE GLboolean isVisible(const Vertex* vertex) { + if(!vertex) return GL_FALSE; return vertex->w >= 0 && vertex->xyz[2] >= -vertex->w; } -static inline ListIterator* next(ListIterator* it) { - /* Move the list iterator to the next vertex to - * submit. Takes care of clipping the triangle strip - * and perspective dividing the vertex before - * returning */ +static Vertex* interpolate_vertex(const Vertex* v0, const Vertex* v1, Vertex* out) { + /* If v0 is in front of the near plane, and v1 is behind the near plane, this + * generates a vertex *on* the near plane */ - while(--it->count) { - it->current++; + return out; +} - /* Ignore dead vertices */ - if(it->current->flags == DEAD) { - continue; +GL_FORCE_INLINE ListIterator* header_reset(ListIterator* it, Vertex* header) { + it->it = header; + it->visibility = 0; + it->triangle_count = 0; + return it; +} + +GL_FORCE_INLINE Vertex* current_postinc(ListIterator* it) { + if(it->remaining == 0) { + return NULL; + } + + it->remaining--; + return it->current++; +} + +GL_FORCE_INLINE Vertex* push_stack(ListIterator* it) { + return &it->stack[it->stack_idx++]; +} + +GL_FORCE_INLINE GLboolean shift(ListIterator* it, Vertex* new_vertex) { + /* + * Shifts in a new vertex, dropping the oldest. If + * new_vertex is NULL it will return GL_FALSE (but still + * shift) */ + it->triangle_count++; + if(it->triangle_count > 3) it->triangle_count = 3; + + it->triangle[0] = it->triangle[1]; + it->triangle[1] = it->triangle[2]; + it->triangle[2] = new_vertex; + + it->visibility <<= 1; + it->visibility &= 7; + it->visibility += isVisible(new_vertex); + + return new_vertex != NULL; +} + +ListIterator* next(ListIterator* it) { + if(!isVertex(it->current)) { + return header_reset(it, current_postinc(it)); + } else { + /* Make sure we have a full triangle of vertices */ + while(it->triangle_count < 3) { + if(!isVertex(it->current)) { + return header_reset(it, current_postinc(it)); + } + + if(!shift(it, current_postinc(it))) { + /* We reached the end so just + * return the oldest until they're gone */ + it->it = it->triangle[0]; + return (it->it) ? it : NULL; + } } - /* If this is a header, then we submit! */ - it->current_is_vertex = isVertex(it->current); + /* OK, by this point we should have info for a complete triangle + * including visibility */ + switch(it->visibility) { + case B111: + /* Totally visible, return the first vertex */ + it->it = it->triangle[0]; + return it; + break; + case B100: + /* First visible only */ + it->triangle[1] = interpolate_vertex(it->triangle[0], it->triangle[1], push_stack(it)); + it->triangle[2] = interpolate_vertex(it->triangle[0], it->triangle[2], push_stack(it)); + it->visibility = B111; /* All visible now, yay! */ - if(it->current_is_vertex) { - return it; + assert(isVisible(it->triangle[1])); + assert(isVisible(it->triangle[2])); + + it->it = it->triangle[0]; + return it; + break; } - - /* All other vertices are fine */ - return it; } return NULL; } -static inline ListIterator* begin(void* src, int n) { - ListIterator* it = (ListIterator*) malloc(sizeof(ListIterator)); - it->count = n; - it->current = (Vertex*) src; - it->current_is_vertex = GL_FALSE; - return (n) ? it : NULL; -} - -static inline void perspective_divide(Vertex* vertex) { +GL_FORCE_INLINE void perspective_divide(Vertex* vertex) { float f = MATH_Fast_Invert(vertex->w); vertex->xyz[0] *= f; vertex->xyz[1] *= f; @@ -91,7 +141,7 @@ static void pvr_list_submit(void *src, int n) { while(it) { __asm__("pref @%0" : : "r"(it->current + 1)); /* prefetch 64 bytes for next loop */ - if(it->current_is_vertex) { + if(isVertex(it->current)) { perspective_divide(it->current); } diff --git a/GL/flush.h b/GL/flush.h new file mode 100644 index 0000000..705c711 --- /dev/null +++ b/GL/flush.h @@ -0,0 +1,48 @@ +#pragma once + +#include "private.h" + +#define MAX_STACK 3 + +#define B000 0 +#define B111 7 +#define B100 4 +#define B010 2 +#define B001 1 +#define B101 5 +#define B011 3 +#define B110 6 + + +typedef struct { + int remaining; + + /* Current position in the source list */ + Vertex* current; + + /* Vertex to read from (this may not exist in the source list) */ + Vertex* it; + + /* < 8. Bitmask of the last 3 vertices */ + uint8_t visibility; + uint8_t triangle_count; + Vertex* triangle[3]; + + /* Stack of temporary vertices */ + Vertex stack[MAX_STACK]; + int8_t stack_idx; +} ListIterator; + +GL_FORCE_INLINE ListIterator* begin(void* src, int n) { + ListIterator* it = (ListIterator*) malloc(sizeof(ListIterator)); + it->remaining = n; + it->current = (Vertex*) src; + it->stack_idx = -1; + it->triangle_count = 0; + it->visibility = 0; + return (n) ? it : NULL; +} + +extern ListIterator* next(ListIterator* it); + + diff --git a/GL/version.h b/GL/version.h index 7dbdf22..57987e0 100644 --- a/GL/version.h +++ b/GL/version.h @@ -1,3 +1,2 @@ #pragma once - -#define GLDC_VERSION "1.2.0alpha" \ No newline at end of file +#define GLDC_VERSION "" diff --git a/tests/Makefile b/tests/Makefile index 4e1e181..b4e3b2c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -16,7 +16,6 @@ rm-elf: test_runner.cpp: $(shell python3 test_generator.py --output=test_runner.cpp $(TESTS)) - @echo MY_VAR IS $(TESTS) test_runner.o: test_runner.cpp diff --git a/tests/test_nearz_clipping.h b/tests/test_nearz_clipping.h index b1e4f89..bdd2514 100644 --- a/tests/test_nearz_clipping.h +++ b/tests/test_nearz_clipping.h @@ -1,14 +1,80 @@ #pragma once #include "../utils/test.h" - +#include "../GL/flush.h" namespace { +struct VertexBuilder { + VertexBuilder& add_header() { + Vertex v; + v.flags = 100; // I dunno what this bit of memory would be + list_.push_back(std::move(v)); + } + + VertexBuilder& add(float x, float y, float z, float w) { + Vertex v; + v.xyz[0] = x; + v.xyz[1] = y; + v.xyz[2] = z; + v.w = w; + } + + std::vector done() { + return list_; + } + +private: + std::vector list_; +}; + class NearZClippingTests : public gldc::test::GLdcTestCase { public: - void test_failure() { - assert_false(true); + void test_clipping_100() { + VertexBuilder builder; + + std::vector list = builder. + add_header(). + add(1, 1, 2, 1). + add(1, 0, 2, -1). + add(0, 1, 2, -1).done(); + + ListIterator* it = begin(&list[0], list.size()); + + Vertex* v0 = it->current; + assert_is_not_null(v0); + it = next(it); + + Vertex* v1 = it->current; + assert_is_not_null(v1); + } + + void test_clipping_110() { + + } + + void test_clipping_111() { + + } + + void test_clipping_101() { + + } + + void test_clipping_010() { + + } + + void test_clipping_001() { + + } + + void test_clipping_000() { + + } + + void test_clipping_011() { + } };