- Added a "vertex map" for ZDBSP's vertex selection. (Think BLOCKMAP for

vertices instead of lines.) On large maps, this can result in a very
  significant speed up. (In one particular map, ZDBSP had previously
  spent 40% of its time just scanning through all the vertices in the
  map. Now the time it spends finding vertices is immeasurable. Now 68%
  of its time on that map is spent inside ClassifyLine--because that
  routine is called more than 16 million times, which suggests that even
  a minor speedup in that routine should have a big impact--if I could
  just think of how that might happen.) On small maps, this won't make
  much of a difference, because the number of vertices to search was so
  small to begin with.

SVN r167 (trunk)
This commit is contained in:
Randy Heit 2006-06-06 02:45:48 +00:00
parent 30436092ed
commit 6d799f0a1c
7 changed files with 188 additions and 70 deletions

View file

@ -1,10 +1,10 @@
CFLAGS = -Wall -fomit-frame-pointer -Izlib -pipe -ffast-math -MMD
# Optimization flags
CFLAGS += -O3 -fomit-frame-pointer
CFLAGS += -O3 -fomit-frame-pointer -DNDEBUG
# Unoptimization flags
#CFLAGS += -g
#CFLAGS += -g -D_DEBUG
# Processor features flags
CFLAGS += -mtune=i686

View file

@ -177,28 +177,16 @@ void FBlockmapBuilder::BuildBlockmap ()
WORD adder;
int bmapwidth, bmapheight;
int minx, maxx, miny, maxy;
int i;
WORD line;
if (Level.NumVertices <= 0)
return;
// Find map extents for the blockmap
minx = maxx = Level.Vertices[0].x;
miny = maxy = Level.Vertices[0].y;
for (i = 1; i < Level.NumVertices; ++i)
{
if (Level.Vertices[i].x < minx) minx = Level.Vertices[i].x;
else if (Level.Vertices[i].x > maxx) maxx = Level.Vertices[i].x;
if (Level.Vertices[i].y < miny) miny = Level.Vertices[i].y;
else if (Level.Vertices[i].y > maxy) maxy = Level.Vertices[i].y;
}
maxx >>= FRACBITS;
minx >>= FRACBITS;
maxy >>= FRACBITS;
miny >>= FRACBITS;
// Get map extents for the blockmap
minx = Level.MinX >> FRACBITS;
miny = Level.MinY >> FRACBITS;
maxx = Level.MaxX >> FRACBITS;
maxy = Level.MaxY >> FRACBITS;
/*
// DoomBSP did this to give the map a margin when drawing it

View file

@ -164,6 +164,9 @@ struct FLevel
int NumOrgVerts;
fixed_t MinX, MinY, MaxX, MaxY;
void FindMapBounds ();
void RemoveExtraLines ();
void RemoveExtraSides ();
void RemoveExtraSectors ();

View file

@ -33,10 +33,6 @@
// Units are in fixed_ts.
const double SIDE_EPSILON = 6.5536;
// Vertices within this distance of each other vertically and horizontally
// will be considered as the same vertex.
const fixed_t VERTEX_EPSILON = 6;
#define Printf printf
#define STACK_ARGS
@ -49,8 +45,9 @@ const fixed_t VERTEX_EPSILON = 6;
FNodeBuilder::FNodeBuilder (FLevel &level,
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
const char *name, bool makeGLnodes)
: Level (level), SegsStuffed (0), MapName (name)
: Level(level), SegsStuffed(0), MapName(name)
{
VertexMap = new FVertexMap (*this, Level.MinX, Level.MinY, Level.MaxX, Level.MaxY);
GLNodes = makeGLnodes;
FindUsedVertices (Level.Vertices, Level.NumVertices);
MakeSegsFromSides ();
@ -59,6 +56,14 @@ FNodeBuilder::FNodeBuilder (FLevel &level,
BuildTree ();
}
FNodeBuilder::~FNodeBuilder()
{
if (VertexMap != 0)
{
delete VertexMap;
}
}
void FNodeBuilder::BuildTree ()
{
fixed_t bbox[4];
@ -739,10 +744,10 @@ int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1,
int near = (fabs(s_num1) < 17179869184.0) | ((fabs(s_num2) < 17179869184.0) << 1);
if (near)
{
double l = d_dx*d_dx + d_dy*d_dy;
double l = 1.0 / (d_dx*d_dx + d_dy*d_dy);
if (near & 1)
{
double dist = s_num1 * s_num1 / l;
double dist = s_num1 * s_num1 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev1 = 0;
@ -758,7 +763,7 @@ int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1,
}
if (near & 2)
{
double dist = s_num2 * s_num2 / l;
double dist = s_num2 * s_num2 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev2 = 0;
@ -870,7 +875,6 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
FPrivVert newvert;
unsigned int vertnum;
int seg2;
unsigned int i;
//Printf ("%u is cut\n", set);
if (seg->loopnum)
@ -887,26 +891,7 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
newvert.y = Vertices[seg->v1].y;
newvert.x += fixed_t(frac * double(Vertices[seg->v2].x - newvert.x));
newvert.y += fixed_t(frac * double(Vertices[seg->v2].y - newvert.y));
for (i = 0; i < Vertices.Size(); ++i)
{
if (abs(Vertices[i].x - newvert.x) < VERTEX_EPSILON &&
abs(Vertices[i].y - newvert.y) < VERTEX_EPSILON)
// if (uint32_t(Vertices[i].x - newvert.x + VERTEX_EPSILON) < 2u*VERTEX_EPSILON &&
// uint32_t(Vertices[i].y - newvert.y + VERTEX_EPSILON) < 2u*VERTEX_EPSILON)
{
break;
}
}
if (i < Vertices.Size())
{
vertnum = i;
}
else
{
newvert.segs = DWORD_MAX;
newvert.segs2 = DWORD_MAX;
vertnum = (int)Vertices.Push (newvert);
}
vertnum = VertexMap->SelectVertexClose (newvert);
seg2 = SplitSeg (set, vertnum, sidev1);

View file

@ -97,6 +97,37 @@ class FNodeBuilder
DWORD Seg;
bool Forward;
};
// Like a blockmap, but for vertices instead of lines
class FVertexMap
{
public:
FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy);
~FVertexMap ();
int SelectVertexExact (FPrivVert &vert);
int SelectVertexClose (FPrivVert &vert);
private:
FNodeBuilder &MyBuilder;
TArray<int> *VertexGrid;
fixed_t MinX, MinY, MaxX, MaxY;
int BlocksWide, BlocksTall;
enum { BLOCK_SIZE = 256 << FRACBITS };
int InsertVertex (FPrivVert &vert);
inline int GetBlock (fixed_t x, fixed_t y)
{
assert (x >= MinX);
assert (y >= MinY);
assert (x <= MaxX);
assert (y <= MaxY);
return (x - MinX) / BLOCK_SIZE + ((y - MinY) / BLOCK_SIZE) * BlocksWide;
}
};
public:
struct FPolyStart
{
@ -107,6 +138,7 @@ public:
FNodeBuilder (FLevel &level,
TArray<FPolyStart> &polyspots, TArray<FPolyStart> &anchors,
const char *name, bool makeGLnodes);
~FNodeBuilder ();
void GetVertices (WideVertex *&verts, int &count);
void GetNodes (MapNodeEx *&nodes, int &nodeCount,
@ -124,6 +156,8 @@ public:
static int PointOnSide (int x, int y, int x1, int y1, int dx, int dy);
private:
FVertexMap *VertexMap;
TArray<node_t> Nodes;
TArray<subsector_t> Subsectors;
TArray<DWORD> SubsectorSets;
@ -149,7 +183,6 @@ private:
const char *MapName;
void FindUsedVertices (WideVertex *vertices, int max);
int SelectVertexExact (FPrivVert &vertex);
void BuildTree ();
void MakeSegsFromSides ();
int CreateSeg (int linenum, int sidenum);

View file

@ -23,10 +23,15 @@
#include "zdbsp.h"
#include "nodebuild.h"
#include "templates.h"
static const int PO_LINE_START = 1;
static const int PO_LINE_EXPLICIT = 5;
// Vertices within this distance of each other vertically and horizontally
// will be considered as the same vertex.
const fixed_t VERTEX_EPSILON = 6;
#if 0
#define D(x) x
#else
@ -42,31 +47,28 @@ static const int PO_LINE_EXPLICIT = 5;
void FNodeBuilder::FindUsedVertices (WideVertex *oldverts, int max)
{
size_t *map = (size_t *)alloca (max*sizeof(size_t));
int *map = (int *)alloca (max*sizeof(int));
int i;
FPrivVert newvert;
memset (&map[0], -1, sizeof(size_t)*max);
newvert.segs = DWORD_MAX;
newvert.segs2 = DWORD_MAX;
memset (&map[0], -1, sizeof(int)*max);
for (i = 0; i < Level.NumLines; ++i)
{
int v1 = Level.Lines[i].v1;
int v2 = Level.Lines[i].v2;
if (map[v1] == (size_t)-1)
if (map[v1] == -1)
{
newvert.x = oldverts[v1].x;
newvert.y = oldverts[v1].y;
map[v1] = SelectVertexExact (newvert);
map[v1] = VertexMap->SelectVertexExact (newvert);
}
if (map[v2] == (size_t)-1)
if (map[v2] == -1)
{
newvert.x = oldverts[v2].x;
newvert.y = oldverts[v2].y;
map[v2] = SelectVertexExact (newvert);
map[v2] = VertexMap->SelectVertexExact (newvert);
}
Level.Lines[i].v1 = (WORD)map[v1];
@ -76,18 +78,6 @@ void FNodeBuilder::FindUsedVertices (WideVertex *oldverts, int max)
Level.NumOrgVerts = (int)InitialVertices;
}
int FNodeBuilder::SelectVertexExact (FPrivVert &vertex)
{
for (unsigned int i = 0; i < Vertices.Size(); ++i)
{
if (Vertices[i].x == vertex.x && Vertices[i].y == vertex.y)
{
return (int)i;
}
}
return (int)Vertices.Push (vertex);
}
// For every sidedef in the map, create a corresponding seg.
void FNodeBuilder::MakeSegsFromSides ()
@ -461,3 +451,99 @@ void FNodeBuilder::AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg)
if (v2->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2->y;
if (v2->y > bbox[BOXTOP]) bbox[BOXTOP] = v2->y;
}
FNodeBuilder::FVertexMap::FVertexMap (FNodeBuilder &builder,
fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy)
: MyBuilder(builder)
{
MinX = minx;
MinY = miny;
BlocksWide = ((maxx - minx + 1) + (BLOCK_SIZE - 1)) / BLOCK_SIZE;
BlocksTall = ((maxy - miny + 1) + (BLOCK_SIZE - 1)) / BLOCK_SIZE;
MaxX = MinX + BlocksWide * BLOCK_SIZE - 1;
MaxY = MinY + BlocksTall * BLOCK_SIZE - 1;
VertexGrid = new TArray<int>[BlocksWide * BlocksTall];
}
FNodeBuilder::FVertexMap::~FVertexMap ()
{
delete[] VertexGrid;
}
int FNodeBuilder::FVertexMap::SelectVertexExact (FNodeBuilder::FPrivVert &vert)
{
TArray<int> &block = VertexGrid[GetBlock (vert.x, vert.y)];
FPrivVert *vertices = &MyBuilder.Vertices[0];
unsigned int i;
for (i = 0; i < block.Size(); ++i)
{
if (vertices[block[i]].x == vert.x && vertices[block[i]].y == vert.y)
{
return (int)i;
}
}
// Not present: add it!
return InsertVertex (vert);
}
int FNodeBuilder::FVertexMap::SelectVertexClose (FNodeBuilder::FPrivVert &vert)
{
TArray<int> &block = VertexGrid[GetBlock (vert.x, vert.y)];
FPrivVert *vertices = &MyBuilder.Vertices[0];
unsigned int i;
for (i = 0; i < block.Size(); ++i)
{
if (abs(vertices[block[i]].x - vert.x) < VERTEX_EPSILON &&
abs(vertices[block[i]].y - vert.y) < VERTEX_EPSILON)
{
return (int)i;
}
}
// Not present: add it!
return InsertVertex (vert);
}
int FNodeBuilder::FVertexMap::InsertVertex (FNodeBuilder::FPrivVert &vert)
{
int vertnum;
vert.segs = DWORD_MAX;
vert.segs2 = DWORD_MAX;
vertnum = (int)MyBuilder.Vertices.Push (vert);
// If a vertex is near a block boundary, then it will be inserted on
// both sides of the boundary so that SelectVertexClose can find
// it by checking in only one block.
fixed_t minx = MAX (MinX, vert.x - VERTEX_EPSILON);
fixed_t maxx = MIN (MaxX, vert.x + VERTEX_EPSILON);
fixed_t miny = MAX (MinY, vert.y - VERTEX_EPSILON);
fixed_t maxy = MIN (MaxY, vert.y + VERTEX_EPSILON);
int blk[4] =
{
GetBlock (minx, miny),
GetBlock (maxx, miny),
GetBlock (minx, maxy),
GetBlock (maxx, maxy)
};
unsigned int blkcount[4] =
{
VertexGrid[blk[0]].Size(),
VertexGrid[blk[1]].Size(),
VertexGrid[blk[2]].Size(),
VertexGrid[blk[3]].Size()
};
for (int i = 0; i < 4; ++i)
{
if (VertexGrid[blk[i]].Size() == blkcount[i])
{
VertexGrid[blk[i]].Push (vertnum);
}
}
return vertnum;
}

View file

@ -90,6 +90,8 @@ FProcessor::FProcessor (FWadReader &inwad, int lump)
{
GetPolySpots ();
}
Level.FindMapBounds ();
}
}
@ -198,6 +200,27 @@ void FProcessor::LoadSectors ()
ReadMapLump<MapSector> (Wad, "SECTORS", Lump, Level.Sectors, Level.NumSectors);
}
void FLevel::FindMapBounds ()
{
fixed_t minx, maxx, miny, maxy;
minx = maxx = Vertices[0].x;
miny = maxy = Vertices[0].y;
for (int i = 1; i < NumVertices; ++i)
{
if (Vertices[i].x < minx) minx = Vertices[i].x;
else if (Vertices[i].x > maxx) maxx = Vertices[i].x;
if (Vertices[i].y < miny) miny = Vertices[i].y;
else if (Vertices[i].y > maxy) maxy = Vertices[i].y;
}
MinX = minx;
MinY = miny;
MaxX = maxx;
MaxY = maxy;
}
void FLevel::RemoveExtraLines ()
{
int i, newNumLines;