- Clean up PolyTriangleDrawer API so that it can better support both softpoly and the swrenderer

- Slightly improve how softpoly processes portals
- Pass the vertex transform matrix via a command rather than being part of the drawer args
- Improve zbuffer drawers in the software renderer
- Misc model rendering fixes
This commit is contained in:
Magnus Norddahl 2018-04-07 15:48:48 +02:00
commit 2d51fa5b43
54 changed files with 1210 additions and 843 deletions

View file

@ -39,52 +39,69 @@
#include "screen_triangle.h"
#include "x86.h"
int PolyTriangleDrawer::viewport_x;
int PolyTriangleDrawer::viewport_y;
int PolyTriangleDrawer::viewport_width;
int PolyTriangleDrawer::viewport_height;
int PolyTriangleDrawer::dest_pitch;
int PolyTriangleDrawer::dest_width;
int PolyTriangleDrawer::dest_height;
uint8_t *PolyTriangleDrawer::dest;
bool PolyTriangleDrawer::dest_bgra;
bool PolyTriangleDrawer::mirror;
void PolyTriangleDrawer::set_viewport(int x, int y, int width, int height, DCanvas *canvas)
void PolyTriangleDrawer::ClearBuffers(DCanvas *canvas)
{
dest = (uint8_t*)canvas->GetBuffer();
dest_width = canvas->GetWidth();
dest_height = canvas->GetHeight();
dest_pitch = canvas->GetPitch();
dest_bgra = canvas->IsBgra();
PolyStencilBuffer::Instance()->Clear(canvas->GetWidth(), canvas->GetHeight(), 0);
PolyZBuffer::Instance()->Resize(canvas->GetPitch(), canvas->GetHeight());
}
void PolyTriangleDrawer::SetViewport(const DrawerCommandQueuePtr &queue, int x, int y, int width, int height, DCanvas *canvas, bool span_drawers)
{
uint8_t *dest = (uint8_t*)canvas->GetBuffer();
int dest_width = canvas->GetWidth();
int dest_height = canvas->GetHeight();
int dest_pitch = canvas->GetPitch();
bool dest_bgra = canvas->IsBgra();
int offsetx = clamp(x, 0, dest_width);
int offsety = clamp(y, 0, dest_height);
int pixelsize = dest_bgra ? 4 : 1;
viewport_x = x - offsetx;
viewport_y = y - offsety;
viewport_width = width;
viewport_height = height;
int viewport_x = x - offsetx;
int viewport_y = y - offsety;
int viewport_width = width;
int viewport_height = height;
dest += (offsetx + offsety * dest_pitch) * pixelsize;
dest_width = clamp(viewport_x + viewport_width, 0, dest_width - offsetx);
dest_height = clamp(viewport_y + viewport_height, 0, dest_height - offsety);
queue->Push<PolySetViewportCommand>(viewport_x, viewport_y, viewport_width, viewport_height, dest, dest_width, dest_height, dest_pitch, dest_bgra, span_drawers);
}
void PolyTriangleDrawer::SetTransform(const DrawerCommandQueuePtr &queue, const Mat4f *objectToClip)
{
queue->Push<PolySetTransformCommand>(objectToClip);
}
void PolyTriangleDrawer::ToggleMirror(const DrawerCommandQueuePtr &queue)
{
queue->Push<PolyToggleMirrorCommand>();
}
/////////////////////////////////////////////////////////////////////////////
void PolyTriangleThreadData::SetViewport(int x, int y, int width, int height, uint8_t *new_dest, int new_dest_width, int new_dest_height, int new_dest_pitch, bool new_dest_bgra, bool new_span_drawers)
{
viewport_x = x;
viewport_y = y;
viewport_width = width;
viewport_height = height;
dest = new_dest;
dest_width = new_dest_width;
dest_height = new_dest_height;
dest_pitch = new_dest_pitch;
dest_bgra = new_dest_bgra;
span_drawers = new_span_drawers;
mirror = false;
}
void PolyTriangleDrawer::toggle_mirror()
void PolyTriangleThreadData::SetTransform(const Mat4f *newObjectToClip)
{
mirror = !mirror;
objectToClip = newObjectToClip;
}
bool PolyTriangleDrawer::is_mirror()
{
return mirror;
}
void PolyTriangleDrawer::draw_elements(const PolyDrawArgs &drawargs, WorkerThreadData *thread)
void PolyTriangleThreadData::DrawElements(const PolyDrawArgs &drawargs)
{
if (drawargs.VertexCount() < 3)
return;
@ -101,7 +118,7 @@ void PolyTriangleDrawer::draw_elements(const PolyDrawArgs &drawargs, WorkerThrea
args.stencilMasks = PolyStencilBuffer::Instance()->Masks();
args.zbuffer = PolyZBuffer::Instance()->Values();
bool ccw = drawargs.FaceCullCCW();
bool ccw = !mirror;
const TriVertex *vinput = drawargs.Vertices();
const unsigned int *elements = drawargs.Elements();
int vcount = drawargs.VertexCount();
@ -112,29 +129,29 @@ void PolyTriangleDrawer::draw_elements(const PolyDrawArgs &drawargs, WorkerThrea
for (int i = 0; i < vcount / 3; i++)
{
for (int j = 0; j < 3; j++)
vert[j] = shade_vertex(drawargs, vinput[*(elements++)]);
draw_shaded_triangle(vert, ccw, &args, thread);
vert[j] = ShadeVertex(drawargs, vinput[*(elements++)]);
DrawShadedTriangle(vert, ccw, &args);
}
}
else if (drawargs.DrawMode() == PolyDrawMode::TriangleFan)
{
vert[0] = shade_vertex(drawargs, vinput[*(elements++)]);
vert[1] = shade_vertex(drawargs, vinput[*(elements++)]);
vert[0] = ShadeVertex(drawargs, vinput[*(elements++)]);
vert[1] = ShadeVertex(drawargs, vinput[*(elements++)]);
for (int i = 2; i < vcount; i++)
{
vert[2] = shade_vertex(drawargs, vinput[*(elements++)]);
draw_shaded_triangle(vert, ccw, &args, thread);
vert[2] = ShadeVertex(drawargs, vinput[*(elements++)]);
DrawShadedTriangle(vert, ccw, &args);
vert[1] = vert[2];
}
}
else // TriangleDrawMode::TriangleStrip
{
vert[0] = shade_vertex(drawargs, vinput[*(elements++)]);
vert[1] = shade_vertex(drawargs, vinput[*(elements++)]);
vert[0] = ShadeVertex(drawargs, vinput[*(elements++)]);
vert[1] = ShadeVertex(drawargs, vinput[*(elements++)]);
for (int i = 2; i < vcount; i++)
{
vert[2] = shade_vertex(drawargs, vinput[*(elements++)]);
draw_shaded_triangle(vert, ccw, &args, thread);
vert[2] = ShadeVertex(drawargs, vinput[*(elements++)]);
DrawShadedTriangle(vert, ccw, &args);
vert[0] = vert[1];
vert[1] = vert[2];
ccw = !ccw;
@ -142,7 +159,7 @@ void PolyTriangleDrawer::draw_elements(const PolyDrawArgs &drawargs, WorkerThrea
}
}
void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadData *thread)
void PolyTriangleThreadData::DrawArrays(const PolyDrawArgs &drawargs)
{
if (drawargs.VertexCount() < 3)
return;
@ -159,7 +176,7 @@ void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadD
args.stencilMasks = PolyStencilBuffer::Instance()->Masks();
args.zbuffer = PolyZBuffer::Instance()->Values();
bool ccw = drawargs.FaceCullCCW();
bool ccw = !mirror;
const TriVertex *vinput = drawargs.Vertices();
int vcount = drawargs.VertexCount();
@ -169,29 +186,29 @@ void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadD
for (int i = 0; i < vcount / 3; i++)
{
for (int j = 0; j < 3; j++)
vert[j] = shade_vertex(drawargs, *(vinput++));
draw_shaded_triangle(vert, ccw, &args, thread);
vert[j] = ShadeVertex(drawargs, *(vinput++));
DrawShadedTriangle(vert, ccw, &args);
}
}
else if (drawargs.DrawMode() == PolyDrawMode::TriangleFan)
{
vert[0] = shade_vertex(drawargs, *(vinput++));
vert[1] = shade_vertex(drawargs, *(vinput++));
vert[0] = ShadeVertex(drawargs, *(vinput++));
vert[1] = ShadeVertex(drawargs, *(vinput++));
for (int i = 2; i < vcount; i++)
{
vert[2] = shade_vertex(drawargs, *(vinput++));
draw_shaded_triangle(vert, ccw, &args, thread);
vert[2] = ShadeVertex(drawargs, *(vinput++));
DrawShadedTriangle(vert, ccw, &args);
vert[1] = vert[2];
}
}
else // TriangleDrawMode::TriangleStrip
{
vert[0] = shade_vertex(drawargs, *(vinput++));
vert[1] = shade_vertex(drawargs, *(vinput++));
vert[0] = ShadeVertex(drawargs, *(vinput++));
vert[1] = ShadeVertex(drawargs, *(vinput++));
for (int i = 2; i < vcount; i++)
{
vert[2] = shade_vertex(drawargs, *(vinput++));
draw_shaded_triangle(vert, ccw, &args, thread);
vert[2] = ShadeVertex(drawargs, *(vinput++));
DrawShadedTriangle(vert, ccw, &args);
vert[0] = vert[1];
vert[1] = vert[2];
ccw = !ccw;
@ -199,12 +216,10 @@ void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadD
}
}
ShadedTriVertex PolyTriangleDrawer::shade_vertex(const PolyDrawArgs &drawargs, const TriVertex &v)
ShadedTriVertex PolyTriangleThreadData::ShadeVertex(const PolyDrawArgs &drawargs, const TriVertex &v)
{
const TriMatrix &objectToClip = *drawargs.ObjectToClip();
// Apply transform to get clip coordinates:
FVector4 position = objectToClip * FVector4(v.x, v.y, v.z, v.w);
Vec4f position = (*objectToClip) * Vec4f(v.x, v.y, v.z, v.w);
ShadedTriVertex sv;
sv.x = position.X;
@ -227,7 +242,7 @@ ShadedTriVertex PolyTriangleDrawer::shade_vertex(const PolyDrawArgs &drawargs, c
return sv;
}
bool PolyTriangleDrawer::is_degenerate(const ShadedTriVertex *vert)
bool PolyTriangleThreadData::IsDegenerate(const ShadedTriVertex *vert)
{
// A degenerate triangle has a zero cross product for two of its sides.
float ax = vert[1].x - vert[0].x;
@ -243,7 +258,7 @@ bool PolyTriangleDrawer::is_degenerate(const ShadedTriVertex *vert)
return crosslengthsqr <= 1.e-6f;
}
bool PolyTriangleDrawer::is_frontfacing(TriDrawTriangleArgs *args)
bool PolyTriangleThreadData::IsFrontfacing(TriDrawTriangleArgs *args)
{
float a =
args->v1->x * args->v2->y - args->v2->x * args->v1->y +
@ -252,15 +267,15 @@ bool PolyTriangleDrawer::is_frontfacing(TriDrawTriangleArgs *args)
return a <= 0.0f;
}
void PolyTriangleDrawer::draw_shaded_triangle(const ShadedTriVertex *vert, bool ccw, TriDrawTriangleArgs *args, WorkerThreadData *thread)
void PolyTriangleThreadData::DrawShadedTriangle(const ShadedTriVertex *vert, bool ccw, TriDrawTriangleArgs *args)
{
// Reject triangle if degenerate
if (is_degenerate(vert))
if (IsDegenerate(vert))
return;
// Cull, clip and generate additional vertices as needed
ShadedTriVertex clippedvert[max_additional_vertices];
int numclipvert = clipedge(vert, clippedvert);
int numclipvert = ClipEdge(vert, clippedvert);
#ifdef NO_SSE
// Map to 2D viewport:
@ -333,8 +348,13 @@ void PolyTriangleDrawer::draw_shaded_triangle(const ShadedTriVertex *vert, bool
args->v1 = &clippedvert[numclipvert - 1];
args->v2 = &clippedvert[i - 1];
args->v3 = &clippedvert[i - 2];
if (is_frontfacing(args) == ccw && args->CalculateGradients())
ScreenTriangle::Draw(args, thread);
if (IsFrontfacing(args) == ccw && args->CalculateGradients())
{
if (!span_drawers)
ScreenTriangle::Draw(args, this);
else
ScreenTriangle::DrawSWRender(args, this);
}
}
}
else
@ -344,13 +364,18 @@ void PolyTriangleDrawer::draw_shaded_triangle(const ShadedTriVertex *vert, bool
args->v1 = &clippedvert[0];
args->v2 = &clippedvert[i - 1];
args->v3 = &clippedvert[i];
if (is_frontfacing(args) != ccw && args->CalculateGradients())
ScreenTriangle::Draw(args, thread);
if (IsFrontfacing(args) != ccw && args->CalculateGradients())
{
if (!span_drawers)
ScreenTriangle::Draw(args, this);
else
ScreenTriangle::DrawSWRender(args, this);
}
}
}
}
int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, ShadedTriVertex *clippedvert)
int PolyTriangleThreadData::ClipEdge(const ShadedTriVertex *verts, ShadedTriVertex *clippedvert)
{
// Clip and cull so that the following is true for all vertices:
// -v.w <= v.x <= v.w
@ -524,35 +549,61 @@ int PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, ShadedTriVertex *
return inputverts;
}
PolyTriangleThreadData *PolyTriangleThreadData::Get(DrawerThread *thread)
{
if (!thread->poly)
thread->poly = std::make_shared<PolyTriangleThreadData>(thread->core, thread->num_cores);
return thread->poly.get();
}
/////////////////////////////////////////////////////////////////////////////
DrawPolyTrianglesCommand::DrawPolyTrianglesCommand(const PolyDrawArgs &args, bool mirror)
: args(args)
PolySetTransformCommand::PolySetTransformCommand(const Mat4f *objectToClip) : objectToClip(objectToClip)
{
}
void PolySetTransformCommand::Execute(DrawerThread *thread)
{
PolyTriangleThreadData::Get(thread)->SetTransform(objectToClip);
}
/////////////////////////////////////////////////////////////////////////////
void PolyToggleMirrorCommand::Execute(DrawerThread *thread)
{
PolyTriangleThreadData::Get(thread)->ToggleMirror();
}
/////////////////////////////////////////////////////////////////////////////
PolySetViewportCommand::PolySetViewportCommand(int x, int y, int width, int height, uint8_t *dest, int dest_width, int dest_height, int dest_pitch, bool dest_bgra, bool span_drawers)
: x(x), y(y), width(width), height(height), dest(dest), dest_width(dest_width), dest_height(dest_height), dest_pitch(dest_pitch), dest_bgra(dest_bgra), span_drawers(span_drawers)
{
}
void PolySetViewportCommand::Execute(DrawerThread *thread)
{
PolyTriangleThreadData::Get(thread)->SetViewport(x, y, width, height, dest, dest_width, dest_height, dest_pitch, dest_bgra, span_drawers);
}
/////////////////////////////////////////////////////////////////////////////
DrawPolyTrianglesCommand::DrawPolyTrianglesCommand(const PolyDrawArgs &args) : args(args)
{
if (mirror)
this->args.SetFaceCullCCW(!this->args.FaceCullCCW());
}
void DrawPolyTrianglesCommand::Execute(DrawerThread *thread)
{
WorkerThreadData thread_data;
thread_data.core = thread->core;
thread_data.num_cores = thread->num_cores;
if (!args.Elements())
PolyTriangleDrawer::draw_arrays(args, &thread_data);
PolyTriangleThreadData::Get(thread)->DrawArrays(args);
else
PolyTriangleDrawer::draw_elements(args, &thread_data);
PolyTriangleThreadData::Get(thread)->DrawElements(args);
}
/////////////////////////////////////////////////////////////////////////////
void DrawRectCommand::Execute(DrawerThread *thread)
{
WorkerThreadData thread_data;
thread_data.core = thread->core;
thread_data.num_cores = thread->num_cores;
auto renderTarget = PolyRenderer::Instance()->RenderTarget;
const void *destOrg = renderTarget->GetBuffer();
int destWidth = renderTarget->GetWidth();
@ -560,7 +611,7 @@ void DrawRectCommand::Execute(DrawerThread *thread)
int destPitch = renderTarget->GetPitch();
int blendmode = (int)args.BlendMode();
if (renderTarget->IsBgra())
ScreenTriangle::RectDrawers32[blendmode](destOrg, destWidth, destHeight, destPitch, &args, &thread_data);
ScreenTriangle::RectDrawers32[blendmode](destOrg, destWidth, destHeight, destPitch, &args, PolyTriangleThreadData::Get(thread));
else
ScreenTriangle::RectDrawers8[blendmode](destOrg, destWidth, destHeight, destPitch, &args, &thread_data);
ScreenTriangle::RectDrawers8[blendmode](destOrg, destWidth, destHeight, destPitch, &args, PolyTriangleThreadData::Get(thread));
}