From 17dc6cc6fc82bde5730022c313d033880ffe3582 Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Thu, 17 Nov 2016 21:03:55 +1100 Subject: [PATCH] Fix degenerate tris showing up in certain configs Occasionally with the right orientation, degenerate triangles will appear as they are no longer zero-area triangles which OGL can cull during rendering. The issue comes from having to manually ensure each primitive in the rendering pipeline sent to the render groups were correctly marked with degenerate vertexes. Instead, we can now just pass the vertex points straight through, using a begin() and end() structure which will append the start and ending degenerate vertexes for us. --- src/Asteroid.c | 3 + src/Renderer.c | 175 +++++++++++++++++++-------------- src/include/Dengine/Renderer.h | 9 ++ 3 files changed, 114 insertions(+), 73 deletions(-) diff --git a/src/Asteroid.c b/src/Asteroid.c index 0a4e7ad..55c3537 100644 --- a/src/Asteroid.c +++ b/src/Asteroid.c @@ -53,6 +53,7 @@ void initRenderer(GameState *state, v2 windowSize) { // NOTE(doyle): Value to map a screen coordinate to NDC coordinate renderer->vertexNdcFactor = V2(1.0f / renderer->size.w, 1.0f / renderer->size.h); + renderer->groupIndexForVertexBatch = -1; const mat4 projection = mat4_ortho(0.0f, renderer->size.w, 0.0f, renderer->size.h, 0.0f, 1.0f); @@ -568,6 +569,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, V4(1.0f, 0, 0, 1.0f), flags); } +#if 1 TrianglePoints triangle = {0}; triangle.points[0] = V2(100, 200); triangle.points[1] = V2(200, 100); @@ -582,6 +584,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, debug_drawUi(state, dt); debug_clearCounter(); +#endif renderer_renderGroups(&state->renderer); } diff --git a/src/Renderer.c b/src/Renderer.c index ca0c07f..51dda3b 100644 --- a/src/Renderer.c +++ b/src/Renderer.c @@ -18,15 +18,68 @@ typedef struct RenderTriangle Vertex vertex[3]; } RenderTriangle_; -INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, - Vertex *vertexList, i32 numVertexes, - enum RenderMode targetRenderMode, - RenderFlags flags) +// NOTE(doyle): A vertex batch is the batch of vertexes comprised to make one +// shape +INTERNAL void beginVertexBatch(Renderer *renderer) { + ASSERT(renderer->vertexBatchState == vertexbatchstate_off); + ASSERT(renderer->groupIndexForVertexBatch == -1); + renderer->vertexBatchState = vertexbatchstate_initial_add; +} -#ifdef DENGINE_DEBUG +/* + NOTE(doyle): Entity rendering is always done in two pairs of + triangles, i.e. quad. To batch render quads as a triangle strip, we + need to create zero-area triangles which OGL will omit from + rendering. Render groups are initialised with 1 degenerate vertex and + then the first two vertexes sent to the render group are the same to + form 1 zero-area triangle strip. + + A degenerate vertex has to be copied from the last vertex in the + rendering quad, to repeat this process as more entities are + renderered. + + Alternative implementation is recognising if the rendered + entity is the first in its render group, then we don't need to init + a degenerate vertex, and only at the end of its vertex list. But on + subsequent renders, we need a degenerate vertex at the front to + create the zero-area triangle strip. + + The first has been chosen for simplicity of code, at the cost of + 1 degenerate vertex at the start of each render group. The initial degenrate + vertexes are added in the add to vertex group and the ending degenerates at + on ending a vertex batch. + */ +INTERNAL void endVertexBatch(Renderer *renderer) +{ + ASSERT(renderer->vertexBatchState != vertexbatchstate_off); + ASSERT(renderer->groupIndexForVertexBatch != -1); + + i32 numDegenerateVertexes = 2; + RenderGroup *group = &renderer->groups[renderer->groupIndexForVertexBatch]; + + i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex; + if (numDegenerateVertexes < freeVertexSlots) + { + Vertex degenerateVertex = group->vertexList[group->vertexIndex-1]; + group->vertexList[group->vertexIndex++] = degenerateVertex; + group->vertexList[group->vertexIndex++] = degenerateVertex; + } + + renderer->vertexBatchState = vertexbatchstate_off; + renderer->groupIndexForVertexBatch = -1; +} + +INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex, + v4 color, Vertex *vertexList, + i32 numVertexes, + enum RenderMode targetRenderMode, + RenderFlags flags) +{ + ASSERT(renderer->vertexBatchState != vertexbatchstate_off); ASSERT(numVertexes > 0); +#ifdef DENGINE_DEBUG for (i32 i = 0; i < numVertexes; i++) debug_countIncrement(debugcount_numVertex); #endif @@ -72,9 +125,6 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, /* New group, unused so initialise it */ groupIsValid = TRUE; - // NOTE(doyle): Mark first vertex as degenerate vertex - group->vertexList[group->vertexIndex++] = vertexList[0]; - group->init = TRUE; group->tex = tex; group->color = color; @@ -89,7 +139,15 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, if (groupIsValid) { i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex; - if (numVertexes < freeVertexSlots) + + // NOTE(doyle): Two at start, two at end + i32 numDegenerateVertexes = 0; + if (renderer->vertexBatchState == vertexbatchstate_initial_add) + { + numDegenerateVertexes = 2; + } + + if ((numDegenerateVertexes + numVertexes) < freeVertexSlots) { if (i != 0) { @@ -106,6 +164,18 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, /* Valid group, add to the render group for rendering */ if (targetGroup) { + if (renderer->vertexBatchState == vertexbatchstate_initial_add) + { + targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[0]; + targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[0]; + renderer->vertexBatchState = vertexbatchstate_active; + + // NOTE(doyle): We swap groups to the front if it is valid, so + // target group should always be 0 + ASSERT(renderer->groupIndexForVertexBatch == -1); + renderer->groupIndexForVertexBatch = 0; + } + for (i32 i = 0; i < numVertexes; i++) { targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[i]; @@ -346,33 +416,10 @@ void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size, RenderQuad_ quad = createRenderQuad(renderer, posInCameraSpace, size, pivotPoint, rotate, *renderTex); - /* - NOTE(doyle): Entity rendering is always done in two pairs of - triangles, i.e. quad. To batch render quads as a triangle strip, we - need to create zero-area triangles which OGL will omit from - rendering. Render groups are initialised with 1 degenerate vertex and - then the first two vertexes sent to the render group are the same to - form 1 zero-area triangle strip. - - A degenerate vertex has to be copied from the last vertex in the - rendering quad, to repeat this process as more entities are - renderered. - - Alternative implementation is recognising if the rendered - entity is the first in its render group, then we don't need to init - a degenerate vertex, and only at the end of its vertex list. But on - subsequent renders, we need a degenerate vertex at the front to - create the zero-area triangle strip. - - The first has been chosen for simplicity of code, at the cost of - 1 degenerate vertex at the start of each render group. - */ - - - Vertex vertexList[6] = {quad.vertex[0], quad.vertex[0], quad.vertex[1], - quad.vertex[2], quad.vertex[3], quad.vertex[3]}; - addVertexToRenderGroup(renderer, renderTex->tex, color, vertexList, - ARRAY_COUNT(vertexList), rendermode_quad, flags); + beginVertexBatch(renderer); + addVertexToRenderGroup_(renderer, renderTex->tex, color, quad.vertex, + ARRAY_COUNT(quad.vertex), rendermode_quad, flags); + endVertexBatch(renderer); } void renderer_polygon(Renderer *const renderer, Rect camera, @@ -395,32 +442,23 @@ void renderer_polygon(Renderer *const renderer, Rect camera, Vertex triangulationBaseVertex = {0}; triangulationBaseVertex.pos = triangulationBaseP; - addVertexToRenderGroup(renderer, renderTex->tex, color, - &triangulationBaseVertex, 1, rendermode_polygon, - flags); - RenderTriangle_ lastRenderTriForDegeneration = {0}; + + beginVertexBatch(renderer); for (i32 i = 1; triangulationIndex < numTrisInTriangulation; i++) { + ASSERT((i + 1) <= numPoints); + RenderTriangle_ tri = {0}; tri.vertex[0].pos = triangulationBaseP; tri.vertex[1].pos = polygonPoints[i + 1]; tri.vertex[2].pos = polygonPoints[i]; - addVertexToRenderGroup(renderer, renderTex->tex, color, tri.vertex, - ARRAY_COUNT(tri.vertex), rendermode_polygon, - flags); - - if (triangulationIndex++ >= numTrisInTriangulation) - { - lastRenderTriForDegeneration = tri; - } - } - for (i32 i = 0; i < 3; i++) - { - addVertexToRenderGroup(renderer, renderTex->tex, color, - &lastRenderTriForDegeneration.vertex[2], 1, - rendermode_polygon, flags); + addVertexToRenderGroup_(renderer, renderTex->tex, color, tri.vertex, + ARRAY_COUNT(tri.vertex), rendermode_polygon, + flags); + triangulationIndex++; } + endVertexBatch(renderer); } void renderer_triangle(Renderer *const renderer, Rect camera, @@ -440,13 +478,11 @@ void renderer_triangle(Renderer *const renderer, Rect camera, RenderTriangle_ renderTriangle = createRenderTriangle( renderer, triangleInCamSpace, pivotPoint, rotate, *renderTex); - // NOTE(doyle): Create degenerate vertex setup - Vertex vertexList[5] = {renderTriangle.vertex[0], renderTriangle.vertex[0], - renderTriangle.vertex[1], renderTriangle.vertex[2], - renderTriangle.vertex[2]}; - - addVertexToRenderGroup(renderer, renderTex->tex, color, vertexList, - ARRAY_COUNT(vertexList), rendermode_triangle, flags); + beginVertexBatch(renderer); + addVertexToRenderGroup_( + renderer, renderTex->tex, color, renderTriangle.vertex, + ARRAY_COUNT(renderTriangle.vertex), rendermode_triangle, flags); + endVertexBatch(renderer); } void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera, @@ -499,20 +535,13 @@ void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera, RenderQuad_ quad = createRenderQuad(renderer, pos, font->maxSize, pivotPoint, rotate, renderTex); - vertexList[vertexIndex++] = quad.vertex[0]; - for (i32 i = 0; i < ARRAY_COUNT(quad.vertex); i++) - { - vertexList[vertexIndex++] = quad.vertex[i]; - } - vertexList[vertexIndex++] = quad.vertex[3]; + beginVertexBatch(renderer); + addVertexToRenderGroup_(renderer, tex, color, quad.vertex, + ARRAY_COUNT(quad.vertex), rendermode_quad, + flags); + endVertexBatch(renderer); pos.x += metric.advance; } - - addVertexToRenderGroup(renderer, tex, color, vertexList, - numVertexesToAlloc, rendermode_quad, flags); - // TODO(doyle): Mem free - // PLATFORM_MEM_FREE(arena, vertexList, - // sizeof(Vertex) * numVertexesToAlloc); } } diff --git a/src/include/Dengine/Renderer.h b/src/include/Dengine/Renderer.h index 639559f..264e7c6 100644 --- a/src/include/Dengine/Renderer.h +++ b/src/include/Dengine/Renderer.h @@ -61,6 +61,12 @@ typedef struct RenderGroup } RenderGroup; +enum VertexBatchState { + vertexbatchstate_off, + vertexbatchstate_initial_add, + vertexbatchstate_active, +}; + typedef struct Renderer { u32 shaderList[shaderlist_count]; @@ -69,6 +75,9 @@ typedef struct Renderer u32 vao[rendermode_count]; u32 vbo[rendermode_count]; + enum VertexBatchState vertexBatchState; + i32 groupIndexForVertexBatch; + i32 numVertexesInVbo; v2 vertexNdcFactor; v2 size;