0

Performance improvements on update scan

This commit is contained in:
Xon
2011-03-19 02:50:44 +08:00
parent 08597ab1c0
commit 383e8197af
6 changed files with 99 additions and 104 deletions

View File

@@ -371,16 +371,16 @@ class ChunkRenderer(object):
# and finally check for a block with same blockid. I we aren't in the border of a chunk, # and finally check for a block with same blockid. I we aren't in the border of a chunk,
# check for the block having the sme blockid. # check for the block having the sme blockid.
if (up_right_blocks != None and up_right_blocks[0,y,z] == blockid) if x == 15 else blocks[x+1,y,z] == blockid: if (up_right_blocks is not None and up_right_blocks[0,y,z] == blockid) if x == 15 else blocks[x+1,y,z] == blockid:
pseudo_data = pseudo_data | 0b1000 pseudo_data = pseudo_data | 0b1000
if (right_blocks != None and right_blocks[x,0,z] == blockid) if y == 15 else blocks[x,y + 1,z] == blockid: if (right_blocks is not None and right_blocks[x,0,z] == blockid) if y == 15 else blocks[x,y + 1,z] == blockid:
pseudo_data = pseudo_data | 0b0100 pseudo_data = pseudo_data | 0b0100
if (left_blocks != None and left_blocks[15,y,z] == blockid) if x == 0 else blocks[x - 1,y,z] == blockid: if (left_blocks is not None and left_blocks[15,y,z] == blockid) if x == 0 else blocks[x - 1,y,z] == blockid:
pseudo_data = pseudo_data | 0b0010 pseudo_data = pseudo_data | 0b0010
if (up_left_blocks != None and up_left_blocks[x,15,z] == blockid) if y == 0 else blocks[x,y - 1,z] == blockid: if (up_left_blocks is not None and up_left_blocks[x,15,z] == blockid) if y == 0 else blocks[x,y - 1,z] == blockid:
pseudo_data = pseudo_data | 0b0001 pseudo_data = pseudo_data | 0b0001
# rotate the bits for other north orientations # rotate the bits for other north orientations
@@ -443,7 +443,7 @@ class ChunkRenderer(object):
# make sure we have a correctly-ranged coordinates and enough # make sure we have a correctly-ranged coordinates and enough
# info about the chunk # info about the chunk
if not (blocks != None and skylight != None and blocklight != None and if not (blocks is not None and skylight is not None and blocklight is not None and
local_x >= 0 and local_x < 16 and local_y >= 0 and local_y < 16 and local_x >= 0 and local_x < 16 and local_y >= 0 and local_y < 16 and
local_z >= 0 and local_z < 128): local_z >= 0 and local_z < 128):
# we have no useful info, return default # we have no useful info, return default

View File

@@ -36,11 +36,11 @@ def alpha_over(dest, src, pos_or_rect=(0, 0), mask=None):
either be a position or a rectangle, specifying where on dest to either be a position or a rectangle, specifying where on dest to
put src. Falls back to dest.paste() if the alpha_over extension put src. Falls back to dest.paste() if the alpha_over extension
can't be found.""" can't be found."""
if mask == None: if mask is None:
mask = src mask = src
global extension_alpha_over global extension_alpha_over
if extension_alpha_over != None: if extension_alpha_over is not None:
# extension ALWAYS expects rects, so convert if needed # extension ALWAYS expects rects, so convert if needed
if len(pos_or_rect) == 2: if len(pos_or_rect) == 2:
pos_or_rect = (pos_or_rect[0], pos_or_rect[1], src.size[0], src.size[1]) pos_or_rect = (pos_or_rect[0], pos_or_rect[1], src.size[0], src.size[1])

View File

@@ -26,6 +26,7 @@ import logging
import util import util
import cPickle import cPickle
import stat import stat
import errno
from time import gmtime, strftime, sleep from time import gmtime, strftime, sleep
from PIL import Image from PIL import Image
@@ -438,7 +439,7 @@ class QuadtreeGen(object):
# return (col, row, chunkx, chunky, regionpath) # return (col, row, chunkx, chunky, regionpath)
chunkx, chunky = self.world.unconvert_coords(col, row) chunkx, chunky = self.world.unconvert_coords(col, row)
c = self.world.get_region_path(chunkx, chunky) c = self.world.get_region_path(chunkx, chunky)
if os.path.exists(c): if c is not None:
chunklist.append((col, row, chunkx, chunky, c)) chunklist.append((col, row, chunkx, chunky, c))
return chunklist return chunklist
@@ -451,53 +452,38 @@ def render_innertile(dest, name, imgformat, optimizeimg):
imgpath = os.path.join(dest, name) + "." + imgformat imgpath = os.path.join(dest, name) + "." + imgformat
if name == "base": if name == "base":
q0path = os.path.join(dest, "0." + imgformat) quadPath = [[(0,0),os.path.join(dest, "0." + imgformat)],[(192,0),os.path.join(dest, "1." + imgformat)], [(0, 192),os.path.join(dest, "2." + imgformat)],[(192,192),os.path.join(dest, "3." + imgformat)]]
q1path = os.path.join(dest, "1." + imgformat)
q2path = os.path.join(dest, "2." + imgformat)
q3path = os.path.join(dest, "3." + imgformat)
else: else:
q0path = os.path.join(dest, name, "0." + imgformat) quadPath = [[(0,0),os.path.join(dest, name, "0." + imgformat)],[(192,0),os.path.join(dest, name, "1." + imgformat)],[(0, 192),os.path.join(dest, name, "2." + imgformat)],[(192,192),os.path.join(dest, name, "3." + imgformat)]]
q1path = os.path.join(dest, name, "1." + imgformat)
q2path = os.path.join(dest, name, "2." + imgformat)
q3path = os.path.join(dest, name, "3." + imgformat)
# Check which ones exist #stat the tile, we need to know if it exists or it's mtime
if not os.path.exists(q0path): try:
q0path = None tile_mtime = os.stat(imgpath)[stat.ST_MTIME];
if not os.path.exists(q1path): except OSError, e:
q1path = None if e.errno != errno.ENOENT:
if not os.path.exists(q2path): raise
q2path = None tile_mtime = None
if not os.path.exists(q3path):
q3path = None
#check mtimes on each part of the quad, this also checks if they exist
needs_rerender = tile_mtime is None
quadPath_filtered = []
for path in quadPath:
try:
quad_mtime = os.stat(path[1])[stat.ST_MTIME];
quadPath_filtered.append(path)
if quad_mtime > tile_mtime:
needs_rerender = True
except OSError:
# We need to stat all the quad files, so keep looping
pass
# do they all not exist? # do they all not exist?
if not (q0path or q1path or q2path or q3path): if quadPath_filtered == []:
if os.path.exists(imgpath): if tile_mtime is not None:
os.unlink(imgpath) os.unlink(imgpath)
return return
# quit now if we don't need rerender
# check the mtimes if not needs_rerender:
try: return
tile_mtime = os.path.getmtime(imgpath)
needs_rerender = False
# remove non-existant paths
components = [q0path, q1path, q2path, q3path]
components = filter(lambda p: p != None, components)
for mtime in [os.path.getmtime(path) for path in components]:
if mtime > tile_mtime:
needs_rerender = True
break
# quit now if we don't need rerender
if not needs_rerender:
return
except OSError:
# one of our mtime calls failed, so we'll continue
pass
#logging.debug("writing out innertile {0}".format(imgpath)) #logging.debug("writing out innertile {0}".format(imgpath))
# Create the actual image now # Create the actual image now
@@ -506,30 +492,12 @@ def render_innertile(dest, name, imgformat, optimizeimg):
# we'll use paste (NOT alpha_over) for quadtree generation because # we'll use paste (NOT alpha_over) for quadtree generation because
# this is just straight image stitching, not alpha blending # this is just straight image stitching, not alpha blending
if q0path: for path in quadPath_filtered:
try: try:
quad0 = Image.open(q0path).resize((192,192), Image.ANTIALIAS) quad = Image.open(path[1]).resize((192,192), Image.ANTIALIAS)
img.paste(quad0, (0,0)) img.paste(quad, path[0])
except Exception, e: except Exception, e:
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", q0path, e) logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", path[1], e)
if q1path:
try:
quad1 = Image.open(q1path).resize((192,192), Image.ANTIALIAS)
img.paste(quad1, (192,0))
except Exception, e:
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", q1path, e)
if q2path:
try:
quad2 = Image.open(q2path).resize((192,192), Image.ANTIALIAS)
img.paste(quad2, (0, 192))
except Exception, e:
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", q2path, e)
if q3path:
try:
quad3 = Image.open(q3path).resize((192,192), Image.ANTIALIAS)
img.paste(quad3, (192, 192))
except Exception, e:
logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", q3path, e)
# Save it # Save it
if imgformat == 'jpg': if imgformat == 'jpg':
@@ -594,12 +562,20 @@ def render_worldtile(quadtree, chunks, colstart, colend, rowstart, rowend, path)
_, _, chunkx, chunky, region = chunk _, _, chunkx, chunky, region = chunk
with open(region, 'rb') as region: with open(region, 'rb') as region:
r = nbt.MCRFileReader(region) r = nbt.MCRFileReader(region)
return r.load_chunk(chunkx, chunky) != None return r.chunkExists(chunkx, chunky)
chunks = filter(chunk_exists, chunks) chunks = filter(chunk_exists, chunks)
#stat the file, we need to know if it exists or it's mtime
try:
tile_mtime = os.stat(imgpath)[stat.ST_MTIME];
except OSError, e:
if e.errno != errno.ENOENT:
raise
tile_mtime = None
if not chunks: if not chunks:
# No chunks were found in this tile # No chunks were found in this tile
if os.path.exists(imgpath): if tile_mtime is not None:
os.unlink(imgpath) os.unlink(imgpath)
return None return None
@@ -612,17 +588,22 @@ def render_worldtile(quadtree, chunks, colstart, colend, rowstart, rowend, path)
# Ignore errno EEXIST: file exists. Since this is multithreaded, # Ignore errno EEXIST: file exists. Since this is multithreaded,
# two processes could conceivably try and create the same directory # two processes could conceivably try and create the same directory
# at the same time. # at the same time.
import errno
if e.errno != errno.EEXIST: if e.errno != errno.EEXIST:
raise raise
# check chunk mtimes to see if they are newer # check chunk mtimes to see if they are newer
try: try:
tile_mtime = os.path.getmtime(imgpath) #tile_mtime = os.path.getmtime(imgpath)
regionMtimes = {}
needs_rerender = False needs_rerender = False
for col, row, chunkx, chunky, regionfile in chunks: for col, row, chunkx, chunky, regionfile in chunks:
# check region file mtime first # check region file mtime first.
if os.path.getmtime(regionfile) <= tile_mtime: # Note: we cache the value since it's actually very likely we will have multipule chunks in the same region, and syscalls are expensive
regionMtime = regionMtimes.get(regionfile,None)
if regionMtime is None:
regionMtime = os.path.getmtime(regionfile)
regionMtimes[regionfile] = regionMtime
if regionMtime <= tile_mtime:
continue continue
# checking chunk mtime # checking chunk mtime

View File

@@ -23,7 +23,7 @@ setup_kwargs['cmdclass'] = {}
# py2exe options # py2exe options
# #
if py2exe != None: if py2exe is not None:
setup_kwargs['console'] = ['gmap.py'] setup_kwargs['console'] = ['gmap.py']
setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png']), setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png']),
('', ['config.js', 'COPYING.txt', 'README.rst']), ('', ['config.js', 'COPYING.txt', 'README.rst']),

View File

@@ -878,15 +878,16 @@ def getBiomeData(worlddir, chunkX, chunkY):
if biomeFile == currentBiomeFile: if biomeFile == currentBiomeFile:
return currentBiomeData return currentBiomeData
f = open(os.path.join(worlddir, "biomes", biomeFile), "rb") try:
rawdata = f.read() with open(os.path.join(worlddir, "biomes", biomeFile), "rb") as f:
f.close() rawdata = f.read()
# make sure the file size is correct
# make sure the file size is correct if not len(rawdata) == 512 * 512 * 2:
if not len(rawdata) == 512 * 512 * 2: raise Exception("Biome file %s is not valid." % (biomeFile,))
raise Exception("Biome file %s is not valid." % (biomeFile,)) data = numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2"))
except IOError:
data = numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2")) data = None
pass # no biome data
currentBiomeFile = biomeFile currentBiomeFile = biomeFile
currentBiomeData = data currentBiomeData = data

View File

@@ -67,10 +67,16 @@ class World(object):
mincol = maxcol = minrow = maxrow = 0 mincol = maxcol = minrow = maxrow = 0
def __init__(self, worlddir, useBiomeData=False): def __init__(self, worlddir, useBiomeData=False,regionlist=None):
self.worlddir = worlddir self.worlddir = worlddir
self.useBiomeData = useBiomeData self.useBiomeData = useBiomeData
#find region files, or load the region list
regionfiles = {}
for x, y, regionfile in self._iterate_regionfiles():
regionfiles[(x,y)] = (x,y,regionfile)
self.regionfiles = regionfiles
# figure out chunk format is in use # figure out chunk format is in use
# if not mcregion, error out early # if not mcregion, error out early
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data'] data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
@@ -102,10 +108,8 @@ class World(object):
def get_region_path(self, chunkX, chunkY): def get_region_path(self, chunkX, chunkY):
"""Returns the path to the region that contains chunk (chunkX, chunkY) """Returns the path to the region that contains chunk (chunkX, chunkY)
""" """
_, _, regionfile = self.regionfiles.get((chunkX//32, chunkY//32),(None,None,None));
chunkFile = "region/r.%s.%s.mcr" % (chunkX//32, chunkY//32) return regionfile
return os.path.join(self.worlddir, chunkFile)
def convert_coords(self, chunkx, chunky): def convert_coords(self, chunkx, chunky):
"""Takes a coordinate (chunkx, chunky) where chunkx and chunky are """Takes a coordinate (chunkx, chunky) where chunkx and chunky are
@@ -168,7 +172,7 @@ class World(object):
# find the dimensions of the map, in region files # find the dimensions of the map, in region files
minx = maxx = miny = maxy = 0 minx = maxx = miny = maxy = 0
found_regions = False found_regions = False
for x, y, regionfile in self._iterate_regionfiles(): for x, y in self.regionfiles:
found_regions = True found_regions = True
minx = min(minx, x) minx = min(minx, x)
maxx = max(maxx, x) maxx = max(maxx, x)
@@ -203,18 +207,27 @@ class World(object):
self.findTrueSpawn() self.findTrueSpawn()
def _iterate_regionfiles(self): def _iterate_regionfiles(self,regionlist=None):
"""Returns an iterator of all of the region files, along with their """Returns an iterator of all of the region files, along with their
coordinates coordinates
Returns (regionx, regiony, filename)""" Returns (regionx, regiony, filename)"""
join = os.path.join
for dirpath, dirnames, filenames in os.walk(os.path.join(self.worlddir, 'region')): if regionlist is not None:
if not dirnames and filenames and "DIM-1" not in dirpath: for path in regionlist:
for f in filenames: if path.endswith("\n"):
if f.startswith("r.") and f.endswith(".mcr"): path = path[:-1]
p = f.split(".") f = os.path.basename(path)
yield (int(p[1]), int(p[2]), os.path.join(dirpath, f)) if f.startswith("r.") and f.endswith(".mcr"):
p = f.split(".")
yield (int(p[1]), int(p[2]), join(self.worlddir, 'region', f))
else:
for dirpath, dirnames, filenames in os.walk(os.path.join(self.worlddir, 'region')):
if not dirnames and filenames and "DIM-1" not in dirpath:
for f in filenames:
if f.startswith("r.") and f.endswith(".mcr"):
p = f.split(".")
yield (int(p[1]), int(p[2]), join(dirpath, f))
def get_save_dir(): def get_save_dir():
"""Returns the path to the local saves directory """Returns the path to the local saves directory