mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-18 10:21:42 +00:00
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:
parent
bf6ab1efc8
commit
4172d70d95
4 changed files with 200 additions and 62 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,70 +285,20 @@ 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++)
|
||||||
{
|
{
|
||||||
DrawSegment *ds = segmentlist->Segment(index);
|
auto &group = segmentlist->SegmentGroups[groupIndex];
|
||||||
|
if (group.x1 >= x2 || group.x2 <= x1)
|
||||||
// [ZZ] portal handling here
|
|
||||||
//if (ds->CurrentPortalUniq != spr->CurrentPortalUniq)
|
|
||||||
// continue;
|
|
||||||
// [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping
|
|
||||||
|
|
||||||
// kg3D - no clipping on fake segs
|
|
||||||
if (ds->fake) continue;
|
|
||||||
// determine if the drawseg obscures the sprite
|
|
||||||
if (ds->x1 >= x2 || ds->x2 <= x1 ||
|
|
||||||
(!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == nullptr &&
|
|
||||||
!ds->bFogBoundary))
|
|
||||||
{
|
|
||||||
// does not cover sprite
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
r1 = MAX<int>(ds->x1, x1);
|
if (group.fardepth < spr->depth)
|
||||||
r2 = MIN<int>(ds->x2, x2);
|
|
||||||
|
|
||||||
float neardepth, fardepth;
|
|
||||||
if (!spr->IsWallSprite())
|
|
||||||
{
|
{
|
||||||
if (ds->sz1 < ds->sz2)
|
r1 = MAX<int>(group.x1, x1);
|
||||||
{
|
r2 = MIN<int>(group.x2, x2);
|
||||||
neardepth = ds->sz1, fardepth = ds->sz2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
neardepth = ds->sz2, fardepth = ds->sz1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Clip bottom
|
||||||
// Check if sprite is in front of draw seg:
|
|
||||||
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.X - ds->curline->v1->fX()) * (ds->curline->v2->fY() - ds->curline->v1->fY()) <= 0))
|
|
||||||
{
|
|
||||||
RenderPortal *renderportal = thread->Portal.get();
|
|
||||||
|
|
||||||
// seg is behind sprite, so draw the mid texture if it has one
|
|
||||||
if (ds->CurrentPortalUniq == renderportal->CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here
|
|
||||||
(ds->maskedtexturecol != nullptr || ds->bFogBoundary))
|
|
||||||
{
|
|
||||||
RenderDrawSegment renderer(thread);
|
|
||||||
renderer.Render(ds, r1, r2);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clip this piece of the sprite
|
|
||||||
// killough 3/27/98: optimized and made much shorter
|
|
||||||
// [RH] Optimized further (at least for VC++;
|
|
||||||
// other compilers should be at least as good as before)
|
|
||||||
|
|
||||||
if (ds->silhouette & SIL_BOTTOM) //bottom sil
|
|
||||||
{
|
|
||||||
clip1 = clipbot + r1;
|
clip1 = clipbot + r1;
|
||||||
clip2 = ds->sprbottomclip + r1 - ds->x1;
|
clip2 = group.sprbottomclip + r1 - group.x1;
|
||||||
i = r2 - r1;
|
i = r2 - r1;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -357,12 +307,10 @@ namespace swrenderer
|
||||||
clip1++;
|
clip1++;
|
||||||
clip2++;
|
clip2++;
|
||||||
} while (--i);
|
} while (--i);
|
||||||
}
|
|
||||||
|
|
||||||
if (ds->silhouette & SIL_TOP) // top sil
|
// Clip top
|
||||||
{
|
|
||||||
clip1 = cliptop + r1;
|
clip1 = cliptop + r1;
|
||||||
clip2 = ds->sprtopclip + r1 - ds->x1;
|
clip2 = group.sprtopclip + r1 - group.x1;
|
||||||
i = r2 - r1;
|
i = r2 - r1;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -372,6 +320,97 @@ namespace swrenderer
|
||||||
clip2++;
|
clip2++;
|
||||||
} while (--i);
|
} 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);
|
||||||
|
|
||||||
|
// [ZZ] portal handling here
|
||||||
|
//if (ds->CurrentPortalUniq != spr->CurrentPortalUniq)
|
||||||
|
// continue;
|
||||||
|
// [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping
|
||||||
|
|
||||||
|
// kg3D - no clipping on fake segs
|
||||||
|
if (ds->fake) continue;
|
||||||
|
// determine if the drawseg obscures the sprite
|
||||||
|
if (ds->x1 >= x2 || ds->x2 <= x1 ||
|
||||||
|
(!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == nullptr &&
|
||||||
|
!ds->bFogBoundary))
|
||||||
|
{
|
||||||
|
// does not cover sprite
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r1 = MAX<int>(ds->x1, x1);
|
||||||
|
r2 = MIN<int>(ds->x2, x2);
|
||||||
|
|
||||||
|
float neardepth, fardepth;
|
||||||
|
if (!spr->IsWallSprite())
|
||||||
|
{
|
||||||
|
if (ds->sz1 < ds->sz2)
|
||||||
|
{
|
||||||
|
neardepth = ds->sz1, fardepth = ds->sz2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
neardepth = ds->sz2, fardepth = ds->sz1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if sprite is in front of draw seg:
|
||||||
|
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.X - ds->curline->v1->fX()) * (ds->curline->v2->fY() - ds->curline->v1->fY()) <= 0))
|
||||||
|
{
|
||||||
|
RenderPortal *renderportal = thread->Portal.get();
|
||||||
|
|
||||||
|
// seg is behind sprite, so draw the mid texture if it has one
|
||||||
|
if (ds->CurrentPortalUniq == renderportal->CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here
|
||||||
|
(ds->maskedtexturecol != nullptr || ds->bFogBoundary))
|
||||||
|
{
|
||||||
|
RenderDrawSegment renderer(thread);
|
||||||
|
renderer.Render(ds, r1, r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clip this piece of the sprite
|
||||||
|
// killough 3/27/98: optimized and made much shorter
|
||||||
|
// [RH] Optimized further (at least for VC++;
|
||||||
|
// other compilers should be at least as good as before)
|
||||||
|
|
||||||
|
if (ds->silhouette & SIL_BOTTOM) //bottom sil
|
||||||
|
{
|
||||||
|
clip1 = clipbot + r1;
|
||||||
|
clip2 = ds->sprbottomclip + r1 - ds->x1;
|
||||||
|
i = r2 - r1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (*clip1 > *clip2)
|
||||||
|
*clip1 = *clip2;
|
||||||
|
clip1++;
|
||||||
|
clip2++;
|
||||||
|
} while (--i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ds->silhouette & SIL_TOP) // top sil
|
||||||
|
{
|
||||||
|
clip1 = cliptop + r1;
|
||||||
|
clip2 = ds->sprtopclip + r1 - ds->x1;
|
||||||
|
i = r2 - r1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (*clip1 < *clip2)
|
||||||
|
*clip1 = *clip2;
|
||||||
|
clip1++;
|
||||||
|
clip2++;
|
||||||
|
} while (--i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// all clipping has been performed, so draw the sprite
|
// all clipping has been performed, so draw the sprite
|
||||||
|
|
Loading…
Reference in a new issue