GFX3D: Vertex post-processing has been moved from the 3D renderers to GFX3D.

- Specifically, viewport transformation, face calculation, and face culling are now handled in GFX3D, and are now standard behaviors for all 3D renderers. This reorganization makes more sense since the 3D renderers are primarily responsible for rasterization and framebuffer post-processing, rather than for processing geometry.
- As a positive side-effect, the OpenGL renderer gains a small performance improvement as well as better accuracy in face culling.
This commit is contained in:
rogerman 2023-02-19 20:23:44 -08:00
parent 5457932d75
commit e1969c470b
7 changed files with 138 additions and 248 deletions

View File

@ -1774,13 +1774,14 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const
}; };
// Set up the initial polygon // Set up the initial polygon
const POLY &initialPoly = rawPolyList[clippedPolyList[firstIndex].index]; const CPoly &initialClippedPoly = clippedPolyList[firstIndex];
TEXIMAGE_PARAM lastTexParams = initialPoly.texParam; const POLY &initialRawPoly = rawPolyList[initialClippedPoly.index];
u32 lastTexPalette = initialPoly.texPalette; TEXIMAGE_PARAM lastTexParams = initialRawPoly.texParam;
GFX3D_Viewport lastViewport = initialPoly.viewport; u32 lastTexPalette = initialRawPoly.texPalette;
GFX3D_Viewport lastViewport = initialRawPoly.viewport;
this->SetupTexture(initialPoly, firstIndex); this->SetupTexture(initialRawPoly, firstIndex);
this->SetupViewport(initialPoly.viewport); this->SetupViewport(initialRawPoly.viewport);
// Enumerate through all polygons and render // Enumerate through all polygons and render
GLsizei vertIndexCount = 0; GLsizei vertIndexCount = 0;
@ -1788,7 +1789,8 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const
for (size_t i = firstIndex; i <= lastIndex; i++) for (size_t i = firstIndex; i <= lastIndex; i++)
{ {
const POLY &rawPoly = rawPolyList[clippedPolyList[i].index]; const CPoly &clippedPoly = clippedPolyList[i];
const POLY &rawPoly = rawPolyList[clippedPoly.index];
// Set up the polygon if it changed // Set up the polygon if it changed
if (lastPolyAttr.value != rawPoly.attribute.value) if (lastPolyAttr.value != rawPoly.attribute.value)
@ -1828,18 +1830,19 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const
// the same and we're not drawing a line loop or line strip. // the same and we're not drawing a line loop or line strip.
if (i+1 <= lastIndex) if (i+1 <= lastIndex)
{ {
const POLY &nextPoly = rawPolyList[clippedPolyList[i+1].index]; const CPoly &nextClippedPoly = clippedPolyList[i+1];
const POLY &nextRawPoly = rawPolyList[nextClippedPoly.index];
if (lastPolyAttr.value == nextPoly.attribute.value && if (lastPolyAttr.value == nextRawPoly.attribute.value &&
lastTexParams.value == nextPoly.texParam.value && lastTexParams.value == nextRawPoly.texParam.value &&
lastTexPalette == nextPoly.texPalette && lastTexPalette == nextRawPoly.texPalette &&
lastViewport.value == nextPoly.viewport.value && lastViewport.value == nextRawPoly.viewport.value &&
polyPrimitive == oglPrimitiveType[nextPoly.vtxFormat] && polyPrimitive == oglPrimitiveType[nextRawPoly.vtxFormat] &&
polyPrimitive != GL_LINE_LOOP && polyPrimitive != GL_LINE_LOOP &&
polyPrimitive != GL_LINE_STRIP && polyPrimitive != GL_LINE_STRIP &&
oglPrimitiveType[nextPoly.vtxFormat] != GL_LINE_LOOP && oglPrimitiveType[nextRawPoly.vtxFormat] != GL_LINE_LOOP &&
oglPrimitiveType[nextPoly.vtxFormat] != GL_LINE_STRIP && oglPrimitiveType[nextRawPoly.vtxFormat] != GL_LINE_STRIP &&
this->_isPolyFrontFacing[i] == this->_isPolyFrontFacing[i+1]) clippedPoly.isPolyBackFacing == nextClippedPoly.isPolyBackFacing)
{ {
continue; continue;
} }
@ -1869,7 +1872,7 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const
rawPoly.attribute.TranslucentDepthWrite_Enable, rawPoly.attribute.TranslucentDepthWrite_Enable,
GFX3D_IsPolyWireframe(rawPoly) || GFX3D_IsPolyOpaque(rawPoly), GFX3D_IsPolyWireframe(rawPoly) || GFX3D_IsPolyOpaque(rawPoly),
rawPoly.attribute.PolygonID, rawPoly.attribute.PolygonID,
this->_isPolyFrontFacing[i]); !clippedPoly.isPolyBackFacing);
} }
else else
{ {
@ -1879,7 +1882,7 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const
rawPoly.attribute.DepthEqualTest_Enable, rawPoly.attribute.DepthEqualTest_Enable,
rawPoly.attribute.TranslucentDepthWrite_Enable, rawPoly.attribute.TranslucentDepthWrite_Enable,
rawPoly.attribute.PolygonID, rawPoly.attribute.PolygonID,
this->_isPolyFrontFacing[i]); !clippedPoly.isPolyBackFacing);
} }
indexBufferPtr += vertIndexCount; indexBufferPtr += vertIndexCount;
@ -3912,7 +3915,6 @@ Render3DError OpenGLRenderer_1_2::ZeroDstAlphaPass(const POLY *rawPolyList, cons
glDisable(GL_BLEND); glDisable(GL_BLEND);
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glStencilFunc(GL_ALWAYS, 0x40, 0x40); glStencilFunc(GL_ALWAYS, 0x40, 0x40);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
@ -4097,7 +4099,6 @@ Render3DError OpenGLRenderer_1_2::ReadBackPixels()
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
glDisable(GL_BLEND); glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID);
@ -4199,15 +4200,9 @@ Render3DError OpenGLRenderer_1_2::BeginRender(const GFX3D_State &renderState, co
for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++) for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++)
{ {
const POLY &rawPoly = this->_rawPolyList[this->_clippedPolyList[i].index]; const CPoly &cPoly = this->_clippedPolyList[i];
const POLY &rawPoly = this->_rawPolyList[cPoly.index];
const size_t polyType = rawPoly.type; const size_t polyType = rawPoly.type;
const VERT vert[4] = {
renderGList.rawVertList[rawPoly.vertIndexes[0]],
renderGList.rawVertList[rawPoly.vertIndexes[1]],
renderGList.rawVertList[rawPoly.vertIndexes[2]],
renderGList.rawVertList[rawPoly.vertIndexes[3]]
};
if (this->isShaderSupported) if (this->isShaderSupported)
{ {
@ -4269,19 +4264,7 @@ Render3DError OpenGLRenderer_1_2::BeginRender(const GFX3D_State &renderState, co
} }
} }
// Get this polygon's facing.
const size_t n = polyType - 1;
float facing = (vert[0].y + vert[n].y) * (vert[0].x - vert[n].x) +
(vert[1].y + vert[0].y) * (vert[1].x - vert[0].x) +
(vert[2].y + vert[1].y) * (vert[2].x - vert[1].x);
for (size_t j = 2; j < n; j++)
{
facing += (vert[j+1].y + vert[j].y) * (vert[j+1].x - vert[j].x);
}
renderNeedsToonTable = (renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT)) && this->isShaderSupported; renderNeedsToonTable = (renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT)) && this->isShaderSupported;
this->_isPolyFrontFacing[i] = (facing < 0);
// Get the texture that is to be attached to this polygon. // Get the texture that is to be attached to this polygon.
this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling); this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling);
@ -4394,6 +4377,7 @@ Render3DError OpenGLRenderer_1_2::RenderGeometry()
{ {
if (this->_clippedPolyCount > 0) if (this->_clippedPolyCount > 0)
{ {
glDisable(GL_CULL_FACE); // Polygons should already be culled before we get here.
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
@ -4488,7 +4472,6 @@ Render3DError OpenGLRenderer_1_2::PostprocessFramebuffer()
// Set up the postprocessing states // Set up the postprocessing states
glViewport(0, 0, (GLsizei)this->_framebufferWidth, (GLsizei)this->_framebufferHeight); glViewport(0, 0, (GLsizei)this->_framebufferWidth, (GLsizei)this->_framebufferHeight);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID);
@ -4803,20 +4786,6 @@ Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool treatAs
// Set up depth test mode // Set up depth test mode
glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS); glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS);
// Set up culling mode
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};
GLenum cullingMode = oglCullingMode[thePoly.attribute.SurfaceCullingMode];
if (cullingMode == 0)
{
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
glCullFace(cullingMode);
}
if (willChangeStencilBuffer) if (willChangeStencilBuffer)
{ {
// Handle drawing states for the polygon // Handle drawing states for the polygon
@ -5162,7 +5131,6 @@ Render3DError OpenGLRenderer_1_2::Reset()
OGLRef.vtxPtrColor = (this->isShaderSupported) ? (GLvoid *)offsetof(VERT, color) : OGLRef.color4fBuffer; OGLRef.vtxPtrColor = (this->isShaderSupported) ? (GLvoid *)offsetof(VERT, color) : OGLRef.color4fBuffer;
memset(&this->_pendingRenderStates, 0, sizeof(this->_pendingRenderStates)); memset(&this->_pendingRenderStates, 0, sizeof(this->_pendingRenderStates));
memset(this->_isPolyFrontFacing, 0, sizeof(this->_isPolyFrontFacing));
texCache.Reset(); texCache.Reset();
@ -5472,15 +5440,9 @@ Render3DError OpenGLRenderer_2_0::BeginRender(const GFX3D_State &renderState, co
for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++) for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++)
{ {
const POLY &rawPoly = this->_rawPolyList[this->_clippedPolyList[i].index]; const CPoly &cPoly = this->_clippedPolyList[i];
const POLY &rawPoly = this->_rawPolyList[cPoly.index];
const size_t polyType = rawPoly.type; const size_t polyType = rawPoly.type;
const VERT vert[4] = {
renderGList.rawVertList[rawPoly.vertIndexes[0]],
renderGList.rawVertList[rawPoly.vertIndexes[1]],
renderGList.rawVertList[rawPoly.vertIndexes[2]],
renderGList.rawVertList[rawPoly.vertIndexes[3]]
};
for (size_t j = 0; j < polyType; j++) for (size_t j = 0; j < polyType; j++)
{ {
@ -5504,19 +5466,7 @@ Render3DError OpenGLRenderer_2_0::BeginRender(const GFX3D_State &renderState, co
} }
} }
// Get this polygon's facing.
const size_t n = polyType - 1;
float facing = (vert[0].y + vert[n].y) * (vert[0].x - vert[n].x) +
(vert[1].y + vert[0].y) * (vert[1].x - vert[0].x) +
(vert[2].y + vert[1].y) * (vert[2].x - vert[1].x);
for (size_t j = 2; j < n; j++)
{
facing += (vert[j+1].y + vert[j].y) * (vert[j+1].x - vert[j].x);
}
renderNeedsToonTable = renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT); renderNeedsToonTable = renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT);
this->_isPolyFrontFacing[i] = (facing < 0);
// Get the texture that is to be attached to this polygon. // Get the texture that is to be attached to this polygon.
this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling); this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling);

View File

@ -736,7 +736,6 @@ protected:
bool _enableMultisampledRendering; bool _enableMultisampledRendering;
int _selectedMultisampleSize; int _selectedMultisampleSize;
bool _isPolyFrontFacing[CLIPPED_POLYLIST_SIZE];
size_t _clearImageIndex; size_t _clearImageIndex;
Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16); Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16);

View File

@ -1788,7 +1788,6 @@ Render3DError OpenGLRenderer_3_2::ZeroDstAlphaPass(const POLY *rawPolyList, cons
glDisable(GL_BLEND); glDisable(GL_BLEND);
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glStencilFunc(GL_ALWAYS, 0x40, 0x40); glStencilFunc(GL_ALWAYS, 0x40, 0x40);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
@ -1926,7 +1925,6 @@ Render3DError OpenGLRenderer_3_2::ReadBackPixels()
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
glDisable(GL_BLEND); glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID);
glBindVertexArray(OGLRef.vaoPostprocessStatesID); glBindVertexArray(OGLRef.vaoPostprocessStatesID);
@ -2023,15 +2021,9 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D_State &renderState, co
for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++) for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++)
{ {
const POLY &rawPoly = this->_rawPolyList[this->_clippedPolyList[i].index]; const CPoly &cPoly = this->_clippedPolyList[i];
const POLY &rawPoly = this->_rawPolyList[cPoly.index];
const size_t polyType = rawPoly.type; const size_t polyType = rawPoly.type;
const VERT vert[4] = {
renderGList.rawVertList[rawPoly.vertIndexes[0]],
renderGList.rawVertList[rawPoly.vertIndexes[1]],
renderGList.rawVertList[rawPoly.vertIndexes[2]],
renderGList.rawVertList[rawPoly.vertIndexes[3]]
};
for (size_t j = 0; j < polyType; j++) for (size_t j = 0; j < polyType; j++)
{ {
@ -2055,19 +2047,7 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D_State &renderState, co
} }
} }
// Get the polygon's facing.
const size_t n = polyType - 1;
float facing = (vert[0].y + vert[n].y) * (vert[0].x - vert[n].x) +
(vert[1].y + vert[0].y) * (vert[1].x - vert[0].x) +
(vert[2].y + vert[1].y) * (vert[2].x - vert[1].x);
for (size_t j = 2; j < n; j++)
{
facing += (vert[j+1].y + vert[j].y) * (vert[j+1].x - vert[j].x);
}
renderNeedsToonTable = renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT); renderNeedsToonTable = renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT);
this->_isPolyFrontFacing[i] = (facing < 0);
// Get the texture that is to be attached to this polygon. // Get the texture that is to be attached to this polygon.
this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling); this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling);
@ -2206,7 +2186,6 @@ Render3DError OpenGLRenderer_3_2::PostprocessFramebuffer()
// Set up the postprocessing states // Set up the postprocessing states
glViewport(0, 0, (GLsizei)this->_framebufferWidth, (GLsizei)this->_framebufferHeight); glViewport(0, 0, (GLsizei)this->_framebufferWidth, (GLsizei)this->_framebufferHeight);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID);
glBindVertexArray(OGLRef.vaoPostprocessStatesID); glBindVertexArray(OGLRef.vaoPostprocessStatesID);
@ -2430,20 +2409,6 @@ Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly, bool treatAs
glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS); glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS);
glUniform1f(OGLRef.uniformPolyDepthOffset[this->_geometryProgramFlags.value], 0.0f); glUniform1f(OGLRef.uniformPolyDepthOffset[this->_geometryProgramFlags.value], 0.0f);
// Set up culling mode
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};
GLenum cullingMode = oglCullingMode[thePoly.attribute.SurfaceCullingMode];
if (cullingMode == 0)
{
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
glCullFace(cullingMode);
}
if (willChangeStencilBuffer) if (willChangeStencilBuffer)
{ {
// Handle drawing states for the polygon // Handle drawing states for the polygon

View File

@ -3257,12 +3257,20 @@ static bool gfx3d_ysort_compare(const u16 idx1, const u16 idx2)
return (idx1 < idx2); return (idx1 < idx2);
} }
static FORCEINLINE s32 iround(const float f)
{
return (s32)f; //lol
}
template <ClipperMode CLIPPERMODE> template <ClipperMode CLIPPERMODE>
size_t gfx3d_PerformClipping(const GFX3D_GeometryList &gList, CPoly *outCPolyUnsortedList) size_t gfx3d_PerformClipping(const GFX3D_GeometryList &gList, CPoly *outCPolyUnsortedList)
{ {
size_t clipCount = 0; size_t clipCount = 0;
PolygonType cpType = POLYGON_TYPE_UNDEFINED; PolygonType cpType = POLYGON_TYPE_UNDEFINED;
const float wScalar = (float)CurrentRenderer->GetFramebufferWidth() / (float)GPU_FRAMEBUFFER_NATIVE_WIDTH;
const float hScalar = (float)CurrentRenderer->GetFramebufferHeight() / (float)GPU_FRAMEBUFFER_NATIVE_HEIGHT;
for (size_t polyIndex = 0; polyIndex < gList.rawPolyCount; polyIndex++) for (size_t polyIndex = 0; polyIndex < gList.rawPolyCount; polyIndex++)
{ {
const POLY &rawPoly = gList.rawPolyList[polyIndex]; const POLY &rawPoly = gList.rawPolyList[polyIndex];
@ -3274,11 +3282,106 @@ size_t gfx3d_PerformClipping(const GFX3D_GeometryList &gList, CPoly *outCPolyUns
(rawPoly.type == POLYGON_TYPE_QUAD) ? &gList.rawVertList[rawPoly.vertIndexes[3]] : NULL (rawPoly.type == POLYGON_TYPE_QUAD) ? &gList.rawVertList[rawPoly.vertIndexes[3]] : NULL
}; };
cpType = GFX3D_GenerateClippedPoly<CLIPPERMODE>(polyIndex, rawPoly.type, rawVerts, outCPolyUnsortedList[clipCount]); CPoly &cPoly = outCPolyUnsortedList[clipCount];
if (cpType != POLYGON_TYPE_UNDEFINED)
cpType = GFX3D_GenerateClippedPoly<CLIPPERMODE>(polyIndex, rawPoly.type, rawVerts, cPoly);
if (cpType == POLYGON_TYPE_UNDEFINED)
{ {
clipCount++; continue;
} }
for (size_t j = 0; j < (size_t)cPoly.type; j++)
{
VERT &vtx = cPoly.clipVerts[j];
// TODO: Possible divide by zero with the w-coordinate.
// Is the vertex being read correctly? Is 0 a valid value for w?
// If both of these questions answer to yes, then how does the NDS handle a NaN?
// For now, simply prevent w from being zero.
//
// Test case: Dance scenes in Princess Debut can generate undefined vertices
// when the -ffast-math option (relaxed IEEE754 compliance) is used.
const float vtxW = (vtx.coord[3] != 0.0f) ? vtx.coord[3] : 0.00000001f;
//homogeneous divide
vtx.coord[0] = (vtx.coord[0]+vtxW) / (2*vtxW);
vtx.coord[1] = (vtx.coord[1]+vtxW) / (2*vtxW);
vtx.coord[2] = (vtx.coord[2]+vtxW) / (2*vtxW);
//CONSIDER: do we need to guarantee that these are in bounds? perhaps not.
//vtx.coord[0] = max( 0.0f, min(1.0f, vtx.coord[0]) );
//vtx.coord[1] = max( 0.0f, min(1.0f, vtx.coord[1]) );
//vtx.coord[2] = max( 0.0f, min(1.0f, vtx.coord[2]) );
//viewport transformation
const GFX3D_Viewport theViewport = rawPoly.viewport;
vtx.coord[0] *= (float)theViewport.width;
vtx.coord[0] += (float)theViewport.x;
vtx.coord[0] *= wScalar;
vtx.coord[1] *= (float)theViewport.height;
vtx.coord[1] += (float)theViewport.y;
vtx.coord[1] = 192 - vtx.coord[1];
vtx.coord[1] *= hScalar;
//here is a hack which needs to be removed.
//at some point our shape engine needs these to be converted to "fixed point"
//which is currently just a float
vtx.coord[0] = (float)iround(16.0f * vtx.coord[0]);
vtx.coord[1] = (float)iround(16.0f * vtx.coord[1]);
if (CLIPPERMODE != ClipperMode_DetermineClipOnly)
{
vtx.texcoord[0] /= vtxW;
vtx.texcoord[1] /= vtxW;
//perspective-correct the colors
vtx.fcolor[0] /= vtxW;
vtx.fcolor[1] /= vtxW;
vtx.fcolor[2] /= vtxW;
}
}
// Perform face culling.
//an older approach
//(not good enough for quads and other shapes)
//float ab[2], ac[2]; Vector2Copy(ab, verts[1].coord); Vector2Copy(ac, verts[2].coord); Vector2Subtract(ab, verts[0].coord);
//Vector2Subtract(ac, verts[0].coord); float cross = Vector2Cross(ab, ac); polyAttr.backfacing = (cross>0);
//a better approach
// we have to support somewhat non-convex polygons (see NSMB world map 1st screen).
// this version should handle those cases better.
const VERT *vtx = cPoly.clipVerts;
const size_t n = cPoly.type - 1;
float facing = (vtx[0].y + vtx[n].y) * (vtx[0].x - vtx[n].x) +
(vtx[1].y + vtx[0].y) * (vtx[1].x - vtx[0].x) +
(vtx[2].y + vtx[1].y) * (vtx[2].x - vtx[1].x);
for (size_t j = 2; j < n; j++)
{
facing += (vtx[j+1].y + vtx[j].y) * (vtx[j+1].x - vtx[j].x);
}
cPoly.isPolyBackFacing = (facing < 0);
static const bool visibleFunction[2][4] = {
//always false, backfacing, !backfacing, always true
{ false, false, true, true },
{ false, true, false, true }
};
const u8 cullingMode = rawPoly.attribute.SurfaceCullingMode;
const bool isPolyVisible = visibleFunction[(cPoly.isPolyBackFacing) ? 1 : 0][cullingMode];
if (!isPolyVisible)
{
// If the polygon is to be culled, then don't count it.
continue;
}
// Incrementing the count will keep the polygon in the list.
clipCount++;
} }
return clipCount; return clipCount;

View File

@ -748,6 +748,7 @@ struct CPoly
{ {
u16 index; // The index number of this polygon in the full polygon list. u16 index; // The index number of this polygon in the full polygon list.
PolygonType type; //otherwise known as "count" of verts PolygonType type; //otherwise known as "count" of verts
bool isPolyBackFacing;
VERT clipVerts[MAX_CLIPPED_VERTS]; VERT clipVerts[MAX_CLIPPED_VERTS];
NDSVertex clipVtxFixed[MAX_CLIPPED_VERTS]; NDSVertex clipVtxFixed[MAX_CLIPPED_VERTS];
}; };

View File

@ -543,7 +543,7 @@ FORCEINLINE void RasterizerUnit<RENDERER>::_pixel(const POLYGON_ATTR polyAttr, c
depthFail = true; depthFail = true;
} }
} }
else if ( (ISFRONTFACING && (dstAttributePolyFacing == PolyFacing_Back)) && (dstColor.a == 0x1F)) else if ( (ISFRONTFACING && (dstAttributePolyFacing == PolyFacing_Back)) && (dstColor.a == 0x1F) )
{ {
// The LEQUAL test is used in the special case where an incoming front-facing polygon's pixel // The LEQUAL test is used in the special case where an incoming front-facing polygon's pixel
// is to be drawn on top of a back-facing polygon's opaque pixel. // is to be drawn on top of a back-facing polygon's opaque pixel.
@ -1289,8 +1289,7 @@ FORCEINLINE void RasterizerUnit<RENDERER>::Render()
for (size_t i = 0; i < clippedPolyCount; i++) for (size_t i = 0; i < clippedPolyCount; i++)
{ {
if (!RENDERER) _debug_thisPoly = (i == this->_softRender->_debug_drawClippedUserPoly); if (!RENDERER) _debug_thisPoly = (i == this->_softRender->_debug_drawClippedUserPoly);
if (!this->_softRender->isPolyVisible[i]) continue;
const CPoly &clippedPoly = this->_softRender->GetClippedPolyByIndex(i); const CPoly &clippedPoly = this->_softRender->GetClippedPolyByIndex(i);
const POLY &rawPoly = rawPolyList[clippedPoly.index]; const POLY &rawPoly = rawPolyList[clippedPoly.index];
const size_t vertCount = (size_t)clippedPoly.type; const size_t vertCount = (size_t)clippedPoly.type;
@ -1311,7 +1310,7 @@ FORCEINLINE void RasterizerUnit<RENDERER>::Render()
for (size_t j = vertCount; j < MAX_CLIPPED_VERTS; j++) for (size_t j = vertCount; j < MAX_CLIPPED_VERTS; j++)
this->_verts[j] = NULL; this->_verts[j] = NULL;
if (!this->_softRender->isPolyBackFacing[i]) if (!clippedPoly.isPolyBackFacing)
{ {
if (polyAttr.Mode == POLYGON_MODE_SHADOW) if (polyAttr.Mode == POLYGON_MODE_SHADOW)
{ {
@ -1382,14 +1381,6 @@ void* SoftRasterizer_RunRasterizerUnit(void *arg)
return 0; return 0;
} }
static void* SoftRasterizer_RunProcessAllVertices(void *arg)
{
SoftRasterizerRenderer *softRender = (SoftRasterizerRenderer *)arg;
softRender->ProcessAllVertices();
return NULL;
}
static void* SoftRasterizer_RunGetAndLoadAllTextures(void *arg) static void* SoftRasterizer_RunGetAndLoadAllTextures(void *arg)
{ {
SoftRasterizerRenderer *softRender = (SoftRasterizerRenderer *)arg; SoftRasterizerRenderer *softRender = (SoftRasterizerRenderer *)arg;
@ -1875,112 +1866,6 @@ ClipperMode SoftRasterizerRenderer::GetPreferredPolygonClippingMode() const
return (this->_enableHighPrecisionColorInterpolation) ? ClipperMode_FullColorInterpolate : ClipperMode_Full; return (this->_enableHighPrecisionColorInterpolation) ? ClipperMode_FullColorInterpolate : ClipperMode_Full;
} }
void SoftRasterizerRenderer::_TransformVertices()
{
const POLY *rawPolyList = this->_rawPolyList;
const float wScalar = (float)this->_framebufferWidth / (float)GPU_FRAMEBUFFER_NATIVE_WIDTH;
const float hScalar = (float)this->_framebufferHeight / (float)GPU_FRAMEBUFFER_NATIVE_HEIGHT;
//viewport transforms
for (size_t i = 0; i < this->_clippedPolyCount; i++)
{
CPoly &cPoly = this->_clippedPolyList[i];
for (size_t j = 0; j < (size_t)cPoly.type; j++)
{
VERT &vert = cPoly.clipVerts[j];
// TODO: Possible divide by zero with the w-coordinate.
// Is the vertex being read correctly? Is 0 a valid value for w?
// If both of these questions answer to yes, then how does the NDS handle a NaN?
// For now, simply prevent w from being zero.
//
// Test case: Dance scenes in Princess Debut can generate undefined vertices
// when the -ffast-math option (relaxed IEEE754 compliance) is used.
const float vertw = (vert.coord[3] != 0.0f) ? vert.coord[3] : 0.00000001f;
//homogeneous divide
vert.coord[0] = (vert.coord[0]+vertw) / (2*vertw);
vert.coord[1] = (vert.coord[1]+vertw) / (2*vertw);
vert.coord[2] = (vert.coord[2]+vertw) / (2*vertw);
vert.texcoord[0] /= vertw;
vert.texcoord[1] /= vertw;
//CONSIDER: do we need to guarantee that these are in bounds? perhaps not.
//vert.coord[0] = max(0.0f,min(1.0f,vert.coord[0]));
//vert.coord[1] = max(0.0f,min(1.0f,vert.coord[1]));
//vert.coord[2] = max(0.0f,min(1.0f,vert.coord[2]));
//perspective-correct the colors
vert.fcolor[0] /= vertw;
vert.fcolor[1] /= vertw;
vert.fcolor[2] /= vertw;
//viewport transformation
const GFX3D_Viewport theViewport = rawPolyList[cPoly.index].viewport;
vert.coord[0] *= theViewport.width;
vert.coord[0] += theViewport.x;
vert.coord[0] *= wScalar;
vert.coord[1] *= theViewport.height;
vert.coord[1] += theViewport.y;
vert.coord[1] = 192 - vert.coord[1];
vert.coord[1] *= hScalar;
//here is a hack which needs to be removed.
//at some point our shape engine needs these to be converted to "fixed point"
//which is currently just a float
vert.coord[0] = (float)iround(16.0f * vert.coord[0]);
vert.coord[1] = (float)iround(16.0f * vert.coord[1]);
}
}
}
void SoftRasterizerRenderer::_GetPolygonStates()
{
static const bool visibleFunction[2][4] = {
//always false, backfacing, !backfacing, always true
{ false, false, true, true },
{ false, true, false, true }
};
const POLY *rawPolyList = this->_rawPolyList;
for (size_t i = 0; i < this->_clippedPolyCount; i++)
{
const CPoly &clippedPoly = this->_clippedPolyList[i];
const POLY &rawPoly = rawPolyList[clippedPoly.index];
const PolygonType polyType = clippedPoly.type;
const VERT *vert = &clippedPoly.clipVerts[0];
const u8 cullingMode = rawPoly.attribute.SurfaceCullingMode;
//HACK: backface culling
//this should be moved to gfx3d, but first we need to redo the way the lists are built
//because it is too convoluted right now.
//(must we throw out verts if a poly gets backface culled? if not, then it might be easier)
//an older approach
//(not good enough for quads and other shapes)
//float ab[2], ac[2]; Vector2Copy(ab, verts[1].coord); Vector2Copy(ac, verts[2].coord); Vector2Subtract(ab, verts[0].coord);
//Vector2Subtract(ac, verts[0].coord); float cross = Vector2Cross(ab, ac); polyAttr.backfacing = (cross>0);
//a better approach
// we have to support somewhat non-convex polygons (see NSMB world map 1st screen).
// this version should handle those cases better.
const size_t n = polyType - 1;
float facing = (vert[0].y + vert[n].y) * (vert[0].x - vert[n].x) +
(vert[1].y + vert[0].y) * (vert[1].x - vert[0].x) +
(vert[2].y + vert[1].y) * (vert[2].x - vert[1].x);
for (size_t j = 2; j < n; j++)
{
facing += (vert[j+1].y + vert[j].y) * (vert[j+1].x - vert[j].x);
}
this->isPolyBackFacing[i] = (facing < 0);
this->isPolyVisible[i] = visibleFunction[this->isPolyBackFacing[i]][cullingMode];
}
}
void SoftRasterizerRenderer::GetAndLoadAllTextures() void SoftRasterizerRenderer::GetAndLoadAllTextures()
{ {
const POLY *rawPolyList = this->_rawPolyList; const POLY *rawPolyList = this->_rawPolyList;
@ -1998,12 +1883,6 @@ void SoftRasterizerRenderer::GetAndLoadAllTextures()
} }
} }
void SoftRasterizerRenderer::ProcessAllVertices()
{
this->_TransformVertices();
this->_GetPolygonStates();
}
Render3DError SoftRasterizerRenderer::ApplyRenderingSettings(const GFX3D_State &renderState) Render3DError SoftRasterizerRenderer::ApplyRenderingSettings(const GFX3D_State &renderState)
{ {
this->_enableHighPrecisionColorInterpolation = CommonSettings.GFX3D_HighResolutionInterpolateColor; this->_enableHighPrecisionColorInterpolation = CommonSettings.GFX3D_HighResolutionInterpolateColor;
@ -2033,12 +1912,10 @@ Render3DError SoftRasterizerRenderer::BeginRender(const GFX3D_State &renderState
if (doMultithreadedStateSetup) if (doMultithreadedStateSetup)
{ {
this->_task[0].execute(&SoftRasterizer_RunGetAndLoadAllTextures, this); this->_task[0].execute(&SoftRasterizer_RunGetAndLoadAllTextures, this);
this->_task[1].execute(&SoftRasterizer_RunProcessAllVertices, this);
} }
else else
{ {
this->GetAndLoadAllTextures(); this->GetAndLoadAllTextures();
this->ProcessAllVertices();
} }
// Convert the toon table colors // Convert the toon table colors

View File

@ -171,8 +171,6 @@ protected:
// SoftRasterizer-specific methods // SoftRasterizer-specific methods
void _UpdateEdgeMarkColorTable(const u16 *edgeMarkColorTable); void _UpdateEdgeMarkColorTable(const u16 *edgeMarkColorTable);
void _UpdateFogTable(const u8 *fogDensityTable); void _UpdateFogTable(const u8 *fogDensityTable);
void _TransformVertices();
void _GetPolygonStates();
// Base rendering methods // Base rendering methods
virtual Render3DError BeginRender(const GFX3D_State &renderState, const GFX3D_GeometryList &renderGList); virtual Render3DError BeginRender(const GFX3D_State &renderState, const GFX3D_GeometryList &renderGList);
@ -186,8 +184,6 @@ public:
int _debug_drawClippedUserPoly; int _debug_drawClippedUserPoly;
CACHE_ALIGN FragmentColor toonColor32LUT[32]; CACHE_ALIGN FragmentColor toonColor32LUT[32];
FragmentAttributesBuffer *_framebufferAttributes; FragmentAttributesBuffer *_framebufferAttributes;
bool isPolyVisible[CLIPPED_POLYLIST_SIZE];
bool isPolyBackFacing[CLIPPED_POLYLIST_SIZE];
GFX3D_State *currentRenderState; GFX3D_State *currentRenderState;
bool _enableFragmentSamplingHack; bool _enableFragmentSamplingHack;
@ -198,7 +194,6 @@ public:
virtual ClipperMode GetPreferredPolygonClippingMode() const; virtual ClipperMode GetPreferredPolygonClippingMode() const;
void GetAndLoadAllTextures(); void GetAndLoadAllTextures();
void ProcessAllVertices();
Render3DError RenderEdgeMarkingAndFog(const SoftRasterizerPostProcessParams &param); Render3DError RenderEdgeMarkingAndFog(const SoftRasterizerPostProcessParams &param);
SoftRasterizerTexture* GetLoadedTextureFromPolygon(const POLY &thePoly, bool enableTexturing); SoftRasterizerTexture* GetLoadedTextureFromPolygon(const POLY &thePoly, bool enableTexturing);