Make Frozen Time rendering 4 times faster by grouping draw segments in batches of 100 (old algorithm processed 32000 draw segs per sprite!)

This commit is contained in:
Magnus Norddahl 2017-02-05 00:42:42 +01:00
parent bf6ab1efc8
commit 4172d70d95
4 changed files with 200 additions and 62 deletions

View file

@ -165,6 +165,7 @@ namespace swrenderer
{ {
CollectPortals(); CollectPortals();
Thread->SpriteList->Sort(); Thread->SpriteList->Sort();
Thread->DrawSegments->BuildSegmentGroups();
Clip3DFloors *clip3d = Thread->Clip3DFloors.get(); Clip3DFloors *clip3d = Thread->Clip3DFloors.get();
if (clip3d->height_top == nullptr) if (clip3d->height_top == nullptr)

View file

@ -40,6 +40,7 @@
#include "swrenderer/things/r_visiblesprite.h" #include "swrenderer/things/r_visiblesprite.h"
#include "swrenderer/scene/r_light.h" #include "swrenderer/scene/r_light.h"
#include "swrenderer/viewport/r_viewport.h" #include "swrenderer/viewport/r_viewport.h"
#include "swrenderer/r_renderthread.h"
namespace swrenderer namespace swrenderer
{ {
@ -83,4 +84,83 @@ namespace swrenderer
{ {
InterestingSegments.Push(segment); InterestingSegments.Push(segment);
} }
void DrawSegmentList::BuildSegmentGroups()
{
SegmentGroups.Clear();
unsigned int groupSize = 100;
for (unsigned int index = BeginIndex(); index < EndIndex(); index += groupSize)
{
auto ds = Segment(index);
DrawSegmentGroup group;
group.BeginIndex = index;
group.EndIndex = MIN(index + groupSize, EndIndex());
group.x1 = ds->x1;
group.x2 = ds->x2;
group.neardepth = MIN(ds->sz1, ds->sz2);
group.fardepth = MAX(ds->sz1, ds->sz2);
for (unsigned int groupIndex = group.BeginIndex + 1; groupIndex < group.EndIndex; groupIndex++)
{
ds = Segment(groupIndex);
group.x1 = MIN(group.x1, ds->x1);
group.x2 = MAX(group.x2, ds->x2);
group.neardepth = MIN(group.neardepth, ds->sz1);
group.neardepth = MIN(group.neardepth, ds->sz2);
group.fardepth = MAX(ds->sz1, group.fardepth);
group.fardepth = MAX(ds->sz2, group.fardepth);
}
for (int x = group.x1; x < group.x2; x++)
{
cliptop[x] = 0;
clipbottom[x] = viewheight;
}
for (unsigned int groupIndex = group.BeginIndex; groupIndex < group.EndIndex; groupIndex++)
{
ds = Segment(groupIndex);
// kg3D - no clipping on fake segs
if (ds->fake) continue;
if (ds->silhouette & SIL_BOTTOM)
{
short *clip1 = clipbottom + ds->x1;
const short *clip2 = ds->sprbottomclip;
int i = ds->x2 - ds->x1;
do
{
if (*clip1 > *clip2)
*clip1 = *clip2;
clip1++;
clip2++;
} while (--i);
}
if (ds->silhouette & SIL_TOP)
{
short *clip1 = cliptop + ds->x1;
const short *clip2 = ds->sprtopclip;
int i = ds->x2 - ds->x1;
do
{
if (*clip1 < *clip2)
*clip1 = *clip2;
clip1++;
clip2++;
} while (--i);
}
}
group.sprtopclip = Thread->FrameMemory->AllocMemory<short>(group.x2 - group.x1);
group.sprbottomclip = Thread->FrameMemory->AllocMemory<short>(group.x2 - group.x1);
memcpy(group.sprtopclip, cliptop + group.x1, (group.x2 - group.x1) * sizeof(short));
memcpy(group.sprbottomclip, clipbottom + group.x1, (group.x2 - group.x1) * sizeof(short));
SegmentGroups.Push(group);
}
}
} }

View file

@ -47,11 +47,23 @@ namespace swrenderer
int CurrentPortalUniq; // [ZZ] to identify the portal that this drawseg is in. used for sprite clipping. int CurrentPortalUniq; // [ZZ] to identify the portal that this drawseg is in. used for sprite clipping.
}; };
struct DrawSegmentGroup
{
short x1, x2;
float neardepth, fardepth;
short *sprtopclip;
short *sprbottomclip;
unsigned int BeginIndex;
unsigned int EndIndex;
};
class DrawSegmentList class DrawSegmentList
{ {
public: public:
DrawSegmentList(RenderThread *thread); DrawSegmentList(RenderThread *thread);
TArray<DrawSegmentGroup> SegmentGroups;
unsigned int BeginIndex() const { return StartIndices.Last(); } unsigned int BeginIndex() const { return StartIndices.Last(); }
unsigned int EndIndex() const { return Segments.Size(); } unsigned int EndIndex() const { return Segments.Size(); }
DrawSegment *Segment(unsigned int index) const { return Segments[Segments.Size() - 1 - index]; } DrawSegment *Segment(unsigned int index) const { return Segments[Segments.Size() - 1 - index]; }
@ -66,6 +78,8 @@ namespace swrenderer
void Push(DrawSegment *segment); void Push(DrawSegment *segment);
void PushInteresting(DrawSegment *segment); void PushInteresting(DrawSegment *segment);
void BuildSegmentGroups();
RenderThread *Thread = nullptr; RenderThread *Thread = nullptr;
private: private:
@ -74,5 +88,9 @@ namespace swrenderer
TArray<DrawSegment *> InterestingSegments; // drawsegs that have something drawn on them TArray<DrawSegment *> InterestingSegments; // drawsegs that have something drawn on them
TArray<unsigned int> StartInterestingIndices; TArray<unsigned int> StartInterestingIndices;
// For building segment groups
short cliptop[MAXWIDTH];
short clipbottom[MAXWIDTH];
}; };
} }

View file

@ -285,7 +285,45 @@ namespace swrenderer
// The first drawseg that is closer than the sprite is the clip seg. // The first drawseg that is closer than the sprite is the clip seg.
DrawSegmentList *segmentlist = thread->DrawSegments.get(); DrawSegmentList *segmentlist = thread->DrawSegments.get();
for (unsigned int index = segmentlist->BeginIndex(); index != segmentlist->EndIndex(); index++) for (unsigned int groupIndex = 0; groupIndex < segmentlist->SegmentGroups.Size(); groupIndex++)
{
auto &group = segmentlist->SegmentGroups[groupIndex];
if (group.x1 >= x2 || group.x2 <= x1)
continue;
if (group.fardepth < spr->depth)
{
r1 = MAX<int>(group.x1, x1);
r2 = MIN<int>(group.x2, x2);
// Clip bottom
clip1 = clipbot + r1;
clip2 = group.sprbottomclip + r1 - group.x1;
i = r2 - r1;
do
{
if (*clip1 > *clip2)
*clip1 = *clip2;
clip1++;
clip2++;
} while (--i);
// Clip top
clip1 = cliptop + r1;
clip2 = group.sprtopclip + r1 - group.x1;
i = r2 - r1;
do
{
if (*clip1 < *clip2)
*clip1 = *clip2;
clip1++;
clip2++;
} while (--i);
}
else
{
//for (unsigned int index = segmentlist->BeginIndex(); index != segmentlist->EndIndex(); index++)
for (unsigned int index = group.BeginIndex; index != group.EndIndex; index++)
{ {
DrawSegment *ds = segmentlist->Segment(index); DrawSegment *ds = segmentlist->Segment(index);
@ -321,7 +359,6 @@ namespace swrenderer
} }
} }
// Check if sprite is in front of draw seg: // Check if sprite is in front of draw seg:
if ((!spr->IsWallSprite() && neardepth > spr->depth) || ((spr->IsWallSprite() || fardepth > spr->depth) && if ((!spr->IsWallSprite() && neardepth > spr->depth) || ((spr->IsWallSprite() || fardepth > spr->depth) &&
(spr->gpos.Y - ds->curline->v1->fY()) * (ds->curline->v2->fX() - ds->curline->v1->fX()) - (spr->gpos.Y - ds->curline->v1->fY()) * (ds->curline->v2->fX() - ds->curline->v1->fX()) -
@ -373,6 +410,8 @@ namespace swrenderer
} while (--i); } while (--i);
} }
} }
}
}
// all clipping has been performed, so draw the sprite // all clipping has been performed, so draw the sprite