264 lines
11 KiB
Python
264 lines
11 KiB
Python
|
|
import numpy
|
|
import sys
|
|
import time
|
|
from mceditlib.selection import BoundingBox
|
|
|
|
from mceditlib.worldeditor import WorldEditor
|
|
from mceditlib.test import templevel
|
|
from mceditlib import relight
|
|
|
|
|
|
def do_copy(dim, station, relight):
|
|
times = 1
|
|
boxes = []
|
|
for x in range(times):
|
|
for z in range(times):
|
|
origin = (x * station.bounds.width, 54, z * station.bounds.length)
|
|
boxes.append(BoundingBox(origin, station.bounds.size))
|
|
dim.copyBlocks(station, station.bounds, origin, create=True, updateLights=relight)
|
|
return reduce(lambda a, b: a.union(b), boxes)
|
|
|
|
def manmade_relight(test):
|
|
world = templevel.TempLevel("AnvilWorld")
|
|
dim = world.getDimension()
|
|
stationEditor = WorldEditor("test_files/station.schematic")
|
|
station = stationEditor.getDimension()
|
|
|
|
startCopy = time.time()
|
|
box = do_copy(dim, station, False)
|
|
copyTime = time.time() - startCopy
|
|
print("Copy took %f seconds. Reducing relight-in-copyBlocks times by this much." % copyTime)
|
|
|
|
positions = []
|
|
for cx, cz in box.chunkPositions():
|
|
for cy in box.sectionPositions(cx, cz):
|
|
positions.append((cx, cy, cz))
|
|
assert len(positions) > box.chunkCount
|
|
|
|
if test == "post" or test == "all":
|
|
def postCopy(): # profiling
|
|
start = time.time()
|
|
count = 0
|
|
print("Relighting outside of copyBlocks. Updating %d cells" % (len(positions) * 16 * 16 * 16))
|
|
for cx, cy, cz in positions:
|
|
indices = numpy.indices((16, 16, 16), numpy.int32)
|
|
indices.shape = 3, 16*16*16
|
|
indices += ([cx << 4], [cy << 4], [cz << 4])
|
|
x, y, z = indices
|
|
relight.updateLightsByCoord(dim, x, y, z)
|
|
count += 1
|
|
t = time.time() - start
|
|
|
|
print "Relight manmade building (outside copyBlocks): " \
|
|
"%d (out of %d) chunk-sections in %.02f seconds (%f sections per second; %dms per section)" \
|
|
% (count, len(positions), t, count / t, 1000 * t / count)
|
|
postCopy()
|
|
|
|
if test == "smart" or test == "all":
|
|
def allSections():
|
|
world = templevel.TempLevel("AnvilWorld")
|
|
dim = world.getDimension()
|
|
|
|
start = time.time()
|
|
do_copy(dim, station, "all")
|
|
t = time.time() - start - copyTime
|
|
|
|
print "Relight manmade building (in copyBlocks, all sections): " \
|
|
"%d chunk-sections in %.02f seconds (%f sections per second; %dms per section)" \
|
|
% (len(positions), t, len(positions) / t, 1000 * t / len(positions))
|
|
allSections()
|
|
|
|
if test == "section" or test == "all":
|
|
def perSection():
|
|
world = templevel.TempLevel("AnvilWorld")
|
|
dim = world.getDimension()
|
|
|
|
start = time.time()
|
|
do_copy(dim, station, "section")
|
|
t = time.time() - start - copyTime
|
|
|
|
print "Relight manmade building (in copyBlocks, for each section): " \
|
|
"%d chunk-sections in %.02f seconds (%f sections per second; %dms per section)" \
|
|
% (len(positions), t, len(positions) / t, 1000 * t / len(positions))
|
|
perSection()
|
|
|
|
if __name__ == '__main__':
|
|
if len(sys.argv) > 1:
|
|
method = sys.argv[1]
|
|
print "Using method", method
|
|
relight.setMethod(method)
|
|
if len(sys.argv) > 2:
|
|
test = sys.argv[2]
|
|
else:
|
|
test = "all"
|
|
manmade_relight(test)
|
|
|
|
|
|
"""
|
|
Conclusion:
|
|
|
|
Much time is spent in the "post" method which updates all cells in the selection box, calling
|
|
updateLights on cells whose opacity values did not change. This is evidenced by the time spent in
|
|
"drawLights", which must be called because updateLights doesn't know the previous block type in
|
|
that cell.
|
|
|
|
copyBlocksFrom has been modified to find the cells whose lighting or opacity value did change,
|
|
and passing only those cells to updateLights. This is more than twice as fast, and updating
|
|
all changed cells at once is even faster, presumably because changes to following chunks will
|
|
invalidate lighting data computed by previous chunks.
|
|
|
|
Because updateLights does not know what the previous cell's opacity values were (it does know the
|
|
cell's current light value, so it can skip spreadLight if the new brightness didn't exceed that),
|
|
clients of updateLights should take care to find only cells whose opacity values changed.
|
|
|
|
copyBlocksFrom stores all changed cell positions, which could lead to MemoryErrors for very large
|
|
copies. Instead of storing all positions, it should periodically call updateLights whenever the
|
|
position list exceeds a threshold. This "batch-update" method should be an acceptable compromise
|
|
between updating for each section (suffering invalidation costs), and updating all sections
|
|
at once after the copy (risking MemoryErrors and possibly paying additional chunk loading costs)
|
|
|
|
Updating lights for chunks whose neighbors have not been copied yet will cause wasted effort.
|
|
It helps to describe this graphically. This is the current visitation order:
|
|
|
|
(area is 24x12, and 34 chunks have been copied so far)
|
|
|
|
************************
|
|
**********..............
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
|
|
'.' represents chunks that are yet to be copied.
|
|
'*' represents chunks that have been copied.
|
|
|
|
If a batched lighting update is called at this point, these are the chunks that, when they are
|
|
copied over later, will invalidate parts of the previous update:
|
|
|
|
************************
|
|
**********--------------
|
|
----------+.............
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
|
|
'-' represents chunks that when edited will invalidate the previous lighting update applied
|
|
to the '*' chunks. There are 24 such chunks.
|
|
|
|
'+' represents chunks that when edited will invalidate at most half of a previous chunk's
|
|
update.
|
|
|
|
So let's say 24.5 chunks are invalidated later. Out of 34 chunks, that is not very good at all.
|
|
|
|
That number is roughly proportional to the width of the selection box.
|
|
|
|
The current visitation order is thus:
|
|
|
|
|
|
1234567890abcdefghijklmn
|
|
opqrstuvwx--------------
|
|
----------+.............
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
|
|
|
|
A possibly improved visitation order:
|
|
|
|
12efghuvwx-.............
|
|
43dcjits--+.............
|
|
589bknor-...............
|
|
670almpq-...............
|
|
--------+...............
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
........................
|
|
|
|
13 full chunks and two half-chunks are invalidated, for a total of 15 chunks out of 34.
|
|
|
|
At least it's less than half.
|
|
|
|
This number is roughly proportional to the square root of the number of chunks copied so far.
|
|
|
|
The order of chunks visited by copyBlocksFrom is linear. When it calls updateLights for a chunk,
|
|
the chunks adjacent to that chunk (and ahead of that chunk in the order) will have to redo part
|
|
of this chunk's lighting for the current chunk when they are copied. To minimize wasted effort,
|
|
a chunk order that resembles a space-filling curve such as a Hilbert curve may be
|
|
applicable. The goal is to reduce the number of chunks who have neighbors yet to be copied at the
|
|
time the batched update is performed.
|
|
|
|
Maybe we can do better. What if, instead of batch-updating ALL of the chunks copied so far,
|
|
we only batch-update the ones we know won't be invalidated later?
|
|
|
|
The cells that need update are currently just tossed in a list. Instead, associate them with
|
|
their chunk position. Keep track of which chunks we have copied, and how many of their
|
|
eight neighbors have already been copied too. Only issue a batch update for chunks where all eight
|
|
neighbors are copied. If we use the original visitation order, then for very large copies, we may
|
|
reach the threshold before any neighbors have been copied. The new visitation order would avoid
|
|
this as, for most chunks, it will visit all of a chunk's neighbors very soon after that chunk.
|
|
|
|
In fact, it may not be necessary to batch-update at all if we can update a chunk as soon as all its
|
|
neighbors are ready.
|
|
|
|
Output:
|
|
Using method cython
|
|
INFO:mceditlib.block_copy:Copying 3103771 blocks from BoundingBox(origin=Vector(0, 0, 0), size=Vector(113, 121, 227)) to (0, 54, 0)
|
|
INFO:mceditlib.block_copy:Copying: Chunk 20/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 40/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 60/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 80/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 100/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 120/120...
|
|
INFO:mceditlib.block_copy:Duration: 1.292s, 120/120 chunks, 10.77ms per chunk (92.88 chunks per second)
|
|
INFO:mceditlib.block_copy:Copied 0/0 entities and 293/293 tile entities
|
|
Copy took 1.292000 seconds. Reducing relight-in-copyBlocks times by this much.
|
|
Relighting outside of copyBlocks. Updating 3932160 cells
|
|
Relight manmade building (outside copyBlocks): 960 (out of 960) chunk-sections in 71.49 seconds (13.428639 sections per second; 74ms per section)
|
|
INFO:mceditlib.block_copy:Copying 3103771 blocks from BoundingBox(origin=Vector(0, 0, 0), size=Vector(113, 121, 227)) to (0, 54, 0)
|
|
INFO:mceditlib.block_copy:Copying: Chunk 20/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 40/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 60/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 80/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 100/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 120/120...
|
|
INFO:mceditlib.block_copy:Duration: 1.318s, 120/120 chunks, 10.98ms per chunk (91.05 chunks per second)
|
|
INFO:mceditlib.block_copy:Copied 0/0 entities and 293/293 tile entities
|
|
INFO:mceditlib.block_copy:Updating all at once for 969 sections (646338 cells)
|
|
INFO:mceditlib.block_copy:Lighting complete.
|
|
INFO:mceditlib.block_copy:Duration: 16.979s, 968 sections, 17.54ms per section (57.01 sections per second)
|
|
Relight manmade building (in copyBlocks, all sections): 960 chunk-sections in 17.01 seconds (56.444027 sections per second; 17ms per section)
|
|
INFO:mceditlib.block_copy:Copying 3103771 blocks from BoundingBox(origin=Vector(0, 0, 0), size=Vector(113, 121, 227)) to (0, 54, 0)
|
|
INFO:mceditlib.block_copy:Copying: Chunk 20/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 40/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 60/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 80/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 100/120...
|
|
INFO:mceditlib.block_copy:Copying: Chunk 120/120...
|
|
Relight manmade building (in copyBlocks, for each section): 960 chunk-sections in 26.12 seconds (36.757667 sections per second; 27ms per section)
|
|
INFO:mceditlib.block_copy:Duration: 27.408s, 120/120 chunks, 228.40ms per chunk (4.38 chunks per second)
|
|
INFO:mceditlib.block_copy:Copied 0/0 entities and 293/293 tile entities
|
|
"""
|