dminer optimization

This commit is contained in:
Vadim Lopatin 2017-05-15 15:54:04 +03:00
parent 1e14826643
commit a66f93d81a
4 changed files with 380 additions and 161 deletions

View File

@ -6,6 +6,7 @@ import dminer.core.world;
import dlangui.graphics.scene.mesh; import dlangui.graphics.scene.mesh;
version = FAST_VISIBILITY_PATH;
// Y range: 0..CHUNK_DY-1 // Y range: 0..CHUNK_DY-1
immutable int CHUNK_DY = 128; immutable int CHUNK_DY = 128;
@ -150,6 +151,7 @@ struct SmallChunk {
protected ulong[8] canPassPlanesX; // 64 bytes WEST to EAST protected ulong[8] canPassPlanesX; // 64 bytes WEST to EAST
protected ulong[8] canPassPlanesY; // 64 bytes DOWN to UP protected ulong[8] canPassPlanesY; // 64 bytes DOWN to UP
protected ulong[8] canPassPlanesZ; // 64 bytes NORTH to SOUTH protected ulong[8] canPassPlanesZ; // 64 bytes NORTH to SOUTH
protected ubyte[6] canPassFromTo; // index is FROM direction, ubyte is DirMask of TO direction; 1 means can pass FROM .. TO
//ulong[6][6] canPassFromTo; // 288 bytes //ulong[6][6] canPassFromTo; // 288 bytes
SmallChunk * [6] nearChunks; SmallChunk * [6] nearChunks;
protected Vector3d _pos; protected Vector3d _pos;
@ -496,11 +498,11 @@ struct SmallChunk {
canPassPlanesZ[z] = canPassFlags; canPassPlanesZ[z] = canPassFlags;
visiblePlanesZ[z] = visibleFlags; visiblePlanesZ[z] = visibleFlags;
} }
// can pass from to // can pass from to
//for (Dir from = Dir.min; from <= Dir.max; ++from) { for (Dir from = Dir.min; from <= Dir.max; ++from) {
// for (Dir to = Dir.min; to <= Dir.max; ++to) { fillCanPassFrom(from);
// } }
//}
dirty = false; dirty = false;
empty = (visiblePlanesZ[0]|visiblePlanesZ[1]|visiblePlanesZ[2]|visiblePlanesZ[3]| empty = (visiblePlanesZ[0]|visiblePlanesZ[1]|visiblePlanesZ[2]|visiblePlanesZ[3]|
visiblePlanesZ[4]|visiblePlanesZ[5]|visiblePlanesZ[6]|visiblePlanesZ[7]) == 0; visiblePlanesZ[4]|visiblePlanesZ[5]|visiblePlanesZ[6]|visiblePlanesZ[7]) == 0;
@ -508,6 +510,128 @@ struct SmallChunk {
dirtyMesh = true; dirtyMesh = true;
} }
/// returns DirMask of available pass direction for specified FROM direction
ubyte getCanPassFromFlags(Dir dirFrom) {
return canPassFromTo[dirFrom];
}
protected void fillCanPassFrom(Dir dirFrom) {
ulong[8] planes;
ulong mask = 0xFFFFFFFFFFFFFFFFL;
ubyte res = 0;
final switch (dirFrom) {
case Dir.NORTH:
for (int i = 7; i >= 0; i--) {
mask = spreadZPlane(mask, canPassPlanesZ[i], DirMask.MASK_ALL);
if (!mask)
break;
planes[i] = mask;
}
if (planes[0])
res |= DirMask.MASK_NORTH;
if (xPlaneFromZplanes(planes, 0))
res |= DirMask.MASK_WEST;
if (xPlaneFromZplanes(planes, 7))
res |= DirMask.MASK_EAST;
if (yPlaneFromZplanes(planes, 0))
res |= DirMask.MASK_DOWN;
if (yPlaneFromZplanes(planes, 7))
res |= DirMask.MASK_UP;
break;
case Dir.SOUTH:
for (int i = 0; i <= 7; i++) {
mask = spreadZPlane(mask, canPassPlanesZ[i], DirMask.MASK_ALL);
if (!mask)
break;
planes[i] = mask;
}
if (planes[7])
res |= DirMask.MASK_SOUTH;
if (xPlaneFromZplanes(planes, 0))
res |= DirMask.MASK_WEST;
if (xPlaneFromZplanes(planes, 7))
res |= DirMask.MASK_EAST;
if (yPlaneFromZplanes(planes, 0))
res |= DirMask.MASK_DOWN;
if (yPlaneFromZplanes(planes, 7))
res |= DirMask.MASK_UP;
break;
case Dir.WEST: // x--
for (int i = 7; i >= 0; i--) {
mask = spreadXPlane(mask, canPassPlanesX[i], DirMask.MASK_ALL);
if (!mask)
break;
planes[i] = mask;
}
if (planes[0])
res |= DirMask.MASK_WEST;
if (zPlaneFromXplanes(planes, 0))
res |= DirMask.MASK_NORTH;
if (zPlaneFromXplanes(planes, 7))
res |= DirMask.MASK_SOUTH;
if (yPlaneFromXplanes(planes, 0))
res |= DirMask.MASK_DOWN;
if (yPlaneFromXplanes(planes, 7))
res |= DirMask.MASK_UP;
break;
case Dir.EAST: // x++
for (int i = 0; i <= 7; i++) {
mask = spreadXPlane(mask, canPassPlanesX[i], DirMask.MASK_ALL);
if (!mask)
break;
planes[i] = mask;
}
if (planes[7])
res |= DirMask.MASK_EAST;
if (zPlaneFromXplanes(planes, 0))
res |= DirMask.MASK_NORTH;
if (zPlaneFromXplanes(planes, 7))
res |= DirMask.MASK_SOUTH;
if (yPlaneFromXplanes(planes, 0))
res |= DirMask.MASK_DOWN;
if (yPlaneFromXplanes(planes, 7))
res |= DirMask.MASK_UP;
break;
case Dir.DOWN: // y--
for (int i = 7; i >= 0; i--) {
mask = spreadYPlane(mask, canPassPlanesY[i], DirMask.MASK_ALL);
if (!mask)
break;
planes[i] = mask;
}
if (planes[0])
res |= DirMask.MASK_DOWN;
if (zPlaneFromYplanes(planes, 0))
res |= DirMask.MASK_NORTH;
if (zPlaneFromYplanes(planes, 7))
res |= DirMask.MASK_SOUTH;
if (xPlaneFromYplanes(planes, 0))
res |= DirMask.MASK_WEST;
if (xPlaneFromYplanes(planes, 7))
res |= DirMask.MASK_EAST;
break;
case Dir.UP: // y--
for (int i = 0; i <= 7; i++) {
mask = spreadYPlane(mask, canPassPlanesY[i], DirMask.MASK_ALL);
if (!mask)
break;
planes[i] = mask;
}
if (planes[7])
res |= DirMask.MASK_UP;
if (zPlaneFromYplanes(planes, 0))
res |= DirMask.MASK_NORTH;
if (zPlaneFromYplanes(planes, 7))
res |= DirMask.MASK_SOUTH;
if (xPlaneFromYplanes(planes, 0))
res |= DirMask.MASK_WEST;
if (xPlaneFromYplanes(planes, 7))
res |= DirMask.MASK_EAST;
break;
}
canPassFromTo[dirFrom] = res;
}
static void spreadFlags(ulong src, ref ulong[8] planes, ref ulong[8] dst, int start, int end, ubyte spreadMask) { static void spreadFlags(ulong src, ref ulong[8] planes, ref ulong[8] dst, int start, int end, ubyte spreadMask) {
if (start < end) { if (start < end) {
for (int i = start; i <= end; ++i) { for (int i = start; i <= end; ++i) {
@ -1206,10 +1330,44 @@ void testPlanes() {
v.testPlanesExtract(); v.testPlanesExtract();
} }
version(FAST_VISIBILITY_PATH) {
struct VisibilityCheckChunk { struct VisibilityCheckChunk {
SmallChunk * chunk; SmallChunk * chunk;
ulong[6] maskFrom; ulong[6] maskFrom;
ulong[6] maskTo; ulong[6] maskTo;
Vector3d pos;
ubyte visitedFromDirMask;
ubyte spreadToDirMask;
void setMask(ulong mask, Dir fromDir) {
maskFrom[fromDir] |= mask;
visitedFromDirMask |= (1 << fromDir);
}
void traceFrom(Dir fromDir) {
ubyte m = chunk ? chunk.getCanPassFromFlags(fromDir) : DirMask.MASK_ALL;
for (ubyte dir = 0; dir < 6; dir++) {
ubyte flag = cast(ubyte)(1 << dir);
if (flag & spreadToDirMask)
if (m & flag)
maskTo[dir] |= 0xFFFFFFFFFFFFFFFFL;
}
}
void tracePaths() {
for (Dir dirFrom = Dir.min; dirFrom <= Dir.max; dirFrom++) {
if ((visitedFromDirMask & (1 << dirFrom)))
traceFrom(dirFrom);
}
}
}
} else {
struct VisibilityCheckChunk {
SmallChunk * chunk;
ulong[6] maskFrom;
ulong[6] maskTo;
Vector3d pos;
ubyte visitedFromDirMask; ubyte visitedFromDirMask;
ubyte spreadToDirMask; ubyte spreadToDirMask;
void setMask(ulong mask, Dir fromDir) { void setMask(ulong mask, Dir fromDir) {
@ -1287,6 +1445,14 @@ struct VisibilityCheckChunk {
void tracePaths() { void tracePaths() {
if (!chunk) {
// empty chunk - assuming transparent
for (ubyte dir = 0; dir < 6; dir++) {
if (spreadToDirMask & (1 << dir))
maskTo[dir] |= 0xFFFFFFFFFFFFFFFFL;
}
return;
}
if (auto mask = maskFrom[Dir.NORTH]) { if (auto mask = maskFrom[Dir.NORTH]) {
ulong[8] planes; ulong[8] planes;
for (int i = 7; i >= 0; i--) { for (int i = 7; i >= 0; i--) {
@ -1352,33 +1518,50 @@ struct VisibilityCheckChunk {
} }
} }
} }
}
/// Diamond iterator for visibility check /// Diamond iterator for visibility check
struct VisibilityCheckIterator { struct VisibilityCheckIterator {
World world; World world;
Vector3d startPos; Vector3d startPos;
Vector3d camPos;
SmallChunk * startChunk; SmallChunk * startChunk;
ChunkVisitor visitor; ChunkVisitor visitor;
int maxDistance;
int maxDistanceSquared;
VisibilityCheckChunk[] plannedChunks; VisibilityCheckChunk[] plannedChunks;
VisibilityCheckChunk[] visitedChunks; VisibilityCheckChunk[] visitedChunks;
VisibilityCheckChunk * getOrAddPlannedChunk(SmallChunk * newChunk) { /// get or add planned chunk by position
VisibilityCheckChunk * getOrAddPlannedChunk(Vector3d pos) {
foreach(ref p; plannedChunks) { foreach(ref p; plannedChunks) {
if (p.chunk is newChunk) if (p.pos == pos)
return &p; return &p;
} }
VisibilityCheckChunk plan; VisibilityCheckChunk plan;
plan.chunk = newChunk; plan.pos = pos;
plannedChunks ~= plan; plannedChunks ~= plan;
return &plannedChunks[$ - 1]; return &plannedChunks[$ - 1];
} }
// step 1: plan visiting chunk // step 1: plan visiting chunk
void planVisitingChunk(int x, int y, int z, Dir fromDir, ulong mask) { void planVisitingChunk(Vector3d p, Dir fromDir, ulong mask) {
// mask test
if (!mask) if (!mask)
return; return;
SmallChunk * newChunk = world.getCellChunk(x, y, z); // distance test
if (!newChunk) Vector3d diff = p - camPos;
if (diff.squaredLength() > maxDistanceSquared)
return; return;
VisibilityCheckChunk * plan = getOrAddPlannedChunk(newChunk); // direction test (TODO)
int dot = diff.dot(cameraDirection);
if (dot < 3000)
return;
//....
// plan visiting
VisibilityCheckChunk * plan = getOrAddPlannedChunk(p);
if (!plan.chunk) {
plan.chunk = world.getCellChunk(p.x, p.y, p.z);
}
plan.setMask(mask, fromDir); plan.setMask(mask, fromDir);
} }
// step 2: visit all planned chunks: move planned to visited; trace paths; plan new visits // step 2: visit all planned chunks: move planned to visited; trace paths; plan new visits
@ -1389,54 +1572,75 @@ struct VisibilityCheckIterator {
foreach (ref p; visitedChunks) { foreach (ref p; visitedChunks) {
visitor.visit(world, p.chunk); visitor.visit(world, p.chunk);
/// set mask of spread directions /// set mask of spread directions
p.spreadToDirMask = calcSpreadMask(p.chunk.position, startPos); p.spreadToDirMask = calcSpreadMask(p.pos, startPos);
p.tracePaths(); p.tracePaths();
ubyte mask = p.spreadToDirMask; ubyte mask = p.spreadToDirMask;
Vector3d pos = p.chunk.position; Vector3d pos = p.pos;
if ((mask & DirMask.MASK_NORTH) && p.maskTo[Dir.NORTH]) { // z-- if ((mask & DirMask.MASK_NORTH) && p.maskTo[Dir.NORTH]) { // z--
planVisitingChunk(pos.x, pos.y, pos.z - 8, Dir.NORTH, p.maskTo[Dir.NORTH]); planVisitingChunk(Vector3d(pos.x, pos.y, pos.z - 8), Dir.NORTH, p.maskTo[Dir.NORTH]);
} }
if ((mask & DirMask.MASK_SOUTH) && p.maskTo[Dir.SOUTH]) { // z++ if ((mask & DirMask.MASK_SOUTH) && p.maskTo[Dir.SOUTH]) { // z++
planVisitingChunk(pos.x, pos.y, pos.z + 8, Dir.SOUTH, p.maskTo[Dir.SOUTH]); planVisitingChunk(Vector3d(pos.x, pos.y, pos.z + 8), Dir.SOUTH, p.maskTo[Dir.SOUTH]);
} }
if ((mask & DirMask.MASK_WEST) && p.maskTo[Dir.WEST]) { // x-- if ((mask & DirMask.MASK_WEST) && p.maskTo[Dir.WEST]) { // x--
planVisitingChunk(pos.x - 8, pos.y, pos.z, Dir.WEST, p.maskTo[Dir.WEST]); planVisitingChunk(Vector3d(pos.x - 8, pos.y, pos.z), Dir.WEST, p.maskTo[Dir.WEST]);
} }
if ((mask & DirMask.MASK_EAST) && p.maskTo[Dir.EAST]) { // x++ if ((mask & DirMask.MASK_EAST) && p.maskTo[Dir.EAST]) { // x++
planVisitingChunk(pos.x + 8, pos.y, pos.z, Dir.EAST, p.maskTo[Dir.EAST]); planVisitingChunk(Vector3d(pos.x + 8, pos.y, pos.z), Dir.EAST, p.maskTo[Dir.EAST]);
} }
if ((mask & DirMask.MASK_DOWN) && p.maskTo[Dir.DOWN]) { // y-- if ((mask & DirMask.MASK_DOWN) && p.maskTo[Dir.DOWN]) { // y--
planVisitingChunk(pos.x, pos.y - 8, pos.z, Dir.DOWN, p.maskTo[Dir.DOWN]); planVisitingChunk(Vector3d(pos.x, pos.y - 8, pos.z), Dir.DOWN, p.maskTo[Dir.DOWN]);
} }
if ((mask & DirMask.MASK_UP) && p.maskTo[Dir.UP]) { // y++ if ((mask & DirMask.MASK_UP) && p.maskTo[Dir.UP]) { // y++
planVisitingChunk(pos.x, pos.y + 8, pos.z, Dir.UP, p.maskTo[Dir.UP]); planVisitingChunk(Vector3d(pos.x, pos.y + 8, pos.z), Dir.UP, p.maskTo[Dir.UP]);
} }
} }
} }
void start(World world, Vector3d startPos) { void start(World world, Vector3d startPos, int maxDistance) {
this.world = world; this.world = world;
this.startChunk = world.getCellChunk(startPos.x, startPos.y, startPos.z); this.startChunk = world.getCellChunk(startPos.x, startPos.y, startPos.z);
this.startPos = this.startChunk.position; // position aligned by 8 cells //if (!startChunk)
// return;
startPos.x &= ~7;
startPos.y &= ~7;
startPos.z &= ~7;
this.startPos = startPos; // position aligned by 8 cells
plannedChunks.assumeSafeAppend; plannedChunks.assumeSafeAppend;
plannedChunks.length = 0; plannedChunks.length = 0;
visitedChunks.assumeSafeAppend; visitedChunks.assumeSafeAppend;
visitedChunks.length = 0; visitedChunks.length = 0;
maxDistanceSquared = maxDistance * maxDistance;
this.maxDistance = maxDistance;
} }
void visitVisibleChunks(ChunkVisitor visitor) { Vector3d cameraDirection;
void visitVisibleChunks(ChunkVisitor visitor, Vector3d cameraDirection) {
this.visitor = visitor; this.visitor = visitor;
this.cameraDirection = cameraDirection;
Vector3d cameraOffset = cameraDirection;
cameraOffset.x /= 16;
cameraOffset.y /= 16;
cameraOffset.z /= 16;
this.camPos = startPos - cameraOffset;
//if (!startChunk)
// return;
visitor.visit(world, startChunk); visitor.visit(world, startChunk);
if (auto mask = startChunk.getSideCanPassToMask(Dir.NORTH)) if (auto mask = startChunk ? startChunk.getSideCanPassToMask(Dir.NORTH) : 0xFFFFFFFFFFFFFFFFL)
planVisitingChunk(startPos.x, startPos.y, startPos.z - 8, Dir.NORTH, mask); planVisitingChunk(Vector3d(startPos.x, startPos.y, startPos.z - 8), Dir.NORTH, mask);
if (auto mask = startChunk.getSideCanPassToMask(Dir.SOUTH)) if (auto mask = startChunk ? startChunk.getSideCanPassToMask(Dir.SOUTH) : 0xFFFFFFFFFFFFFFFFL)
planVisitingChunk(startPos.x, startPos.y, startPos.z + 8, Dir.SOUTH, mask); planVisitingChunk(Vector3d(startPos.x, startPos.y, startPos.z + 8), Dir.SOUTH, mask);
if (auto mask = startChunk.getSideCanPassToMask(Dir.WEST)) if (auto mask = startChunk ? startChunk.getSideCanPassToMask(Dir.WEST) : 0xFFFFFFFFFFFFFFFFL)
planVisitingChunk(startPos.x - 8, startPos.y, startPos.z, Dir.WEST, mask); planVisitingChunk(Vector3d(startPos.x - 8, startPos.y, startPos.z), Dir.WEST, mask);
if (auto mask = startChunk.getSideCanPassToMask(Dir.EAST)) if (auto mask = startChunk ? startChunk.getSideCanPassToMask(Dir.EAST) : 0xFFFFFFFFFFFFFFFFL)
planVisitingChunk(startPos.x + 8, startPos.y, startPos.z, Dir.EAST, mask); planVisitingChunk(Vector3d(startPos.x + 8, startPos.y, startPos.z), Dir.EAST, mask);
if (auto mask = startChunk.getSideCanPassToMask(Dir.DOWN)) if (auto mask = startChunk ? startChunk.getSideCanPassToMask(Dir.DOWN) : 0xFFFFFFFFFFFFFFFFL)
planVisitingChunk(startPos.x, startPos.y - 8, startPos.z, Dir.DOWN, mask); planVisitingChunk(Vector3d(startPos.x, startPos.y - 8, startPos.z), Dir.DOWN, mask);
if (auto mask = startChunk.getSideCanPassToMask(Dir.UP)) if (auto mask = startChunk ? startChunk.getSideCanPassToMask(Dir.UP) : 0xFFFFFFFFFFFFFFFFL)
planVisitingChunk(startPos.x, startPos.y + 8, startPos.z, Dir.UP, mask); planVisitingChunk(Vector3d(startPos.x, startPos.y + 8, startPos.z), Dir.UP, mask);
for (int d = 0; d < maxDistance; d += 5) {
if (!plannedChunks.length)
break;
visitPlannedChunks();
}
} }
} }

View File

@ -180,6 +180,14 @@ struct Vector3d {
} }
return res; return res;
} }
int dot(ref Vector3d v) {
return x * v.x + y * v.y + z * v.z;
}
int squaredLength() {
return x*x + y*y + z*z;
}
} }
__gshared const Vector3d ZERO3 = Vector3d(0, 0, 0); __gshared const Vector3d ZERO3 = Vector3d(0, 0, 0);

View File

@ -7,7 +7,7 @@ import dminer.core.chunk;
version (Android) { version (Android) {
const int MAX_VIEW_DISTANCE = 60; const int MAX_VIEW_DISTANCE = 60;
} else { } else {
const int MAX_VIEW_DISTANCE = 90; const int MAX_VIEW_DISTANCE = 120;
} }

View File

@ -65,6 +65,7 @@ class MinerDrawable : MaterialDrawableObject, ChunkVisitor {
import dlangui.graphics.scene.node; import dlangui.graphics.scene.node;
private World _world; private World _world;
private ChunkDiamondVisitor _chunkVisitor; private ChunkDiamondVisitor _chunkVisitor;
private VisibilityCheckIterator _chunkIterator;
private Vector3d _pos; private Vector3d _pos;
private Node3d _node; private Node3d _node;
private Camera _cam; private Camera _cam;
@ -82,14 +83,20 @@ class MinerDrawable : MaterialDrawableObject, ChunkVisitor {
/// override it /// override it
_node = node; _node = node;
//Log.d("drawing Miner scene"); //Log.d("drawing Miner scene");
_chunkVisitor.init(_world, MAX_VIEW_DISTANCE, this); _chunkIterator.start(_world, _world.camPosition.pos, MAX_VIEW_DISTANCE);
//_chunkVisitor.init(_world, MAX_VIEW_DISTANCE, this);
_pos = _world.camPosition.pos; _pos = _world.camPosition.pos;
_camPosition = _cam.translation; _camPosition = _cam.translation;
_camForwardVector = _cam.forwardVectorWorld; _camForwardVector = _cam.forwardVectorWorld;
_camPosition -= _camForwardVector * 8; _camPosition -= _camForwardVector * 8;
_skippedCount = _drawnCount = 0; _skippedCount = _drawnCount = 0;
long ts = currentTimeMillis(); long ts = currentTimeMillis();
_chunkVisitor.visitChunks(_pos); //_chunkVisitor.visitChunks(_pos);
Vector3d camVector;
camVector.x = cast(int)(_camForwardVector.x * 256);
camVector.y = cast(int)(_camForwardVector.y * 256);
camVector.z = cast(int)(_camForwardVector.z * 256);
_chunkIterator.visitVisibleChunks(this, camVector);
long duration = currentTimeMillis() - ts; long duration = currentTimeMillis() - ts;
Log.d("drawing of Miner scene finished in ", duration, " ms skipped:", _skippedCount, " drawn:", _drawnCount); Log.d("drawing of Miner scene finished in ", duration, " ms skipped:", _skippedCount, " drawn:", _drawnCount);
} }
@ -99,7 +106,7 @@ class MinerDrawable : MaterialDrawableObject, ChunkVisitor {
vec3 chunkPos = vec3(p.x + 4, p.y + 4, p.z + 4); vec3 chunkPos = vec3(p.x + 4, p.y + 4, p.z + 4);
vec3 chunkDirection = (chunkPos - _camPosition).normalized; vec3 chunkDirection = (chunkPos - _camPosition).normalized;
float dot = _camForwardVector.dot(chunkDirection); float dot = _camForwardVector.dot(chunkDirection);
//Log.d("chunkPos ", chunkPos, " chunkDir ", chunkDirection, " camDir "); //Log.d("visit() chunkPos ", chunkPos, " chunkDir ", chunkDirection, " camDir ");
if (dot < 0.7) { // cos(45) if (dot < 0.7) { // cos(45)
_skippedCount++; _skippedCount++;
return; return;