0

fixed strangeness in tile caching code.

I believe the tile caching and hashing mechanisms are working now.
This commit is contained in:
Andrew Brown
2010-09-06 12:58:30 -04:00
parent 54fb6ace07
commit 2d7bd248ed

View File

@@ -216,11 +216,13 @@ def render_worldtile(chunkmap, colstart, colend, rowstart, rowend, oldhash):
object) as returned from render_chunks_async() object) as returned from render_chunks_async()
Return value is (image object, hash) where hash is some string that depends Return value is (image object, hash) where hash is some string that depends
on the image contents. If no tiles were found, the image object is None. on the image contents.
If no tiles were found, (None, hash) is returned.
oldhash is a hash value of an existing tile. The hash of this tile is oldhash is a hash value of an existing tile. The hash of this tile is
computed before it is rendered, and if they match, rendering is skipped and computed before it is rendered, and if they match, rendering is skipped and
(None, oldhash) is returned. (True, oldhash) is returned.
""" """
# width of one chunk is 384. Each column is half a chunk wide. The total # width of one chunk is 384. Each column is half a chunk wide. The total
# width is (384 + 192*(numcols-1)) since the first column contributes full # width is (384 + 192*(numcols-1)) since the first column contributes full
@@ -265,11 +267,13 @@ def render_worldtile(chunkmap, colstart, colend, rowstart, rowend, oldhash):
os.path.basename(chunkfile).split(".")[4] os.path.basename(chunkfile).split(".")[4]
) )
if not tilelist:
return None, imghash.digest()
digest = imghash.digest() digest = imghash.digest()
if not tilelist:
# No chunks were found in this tile
return None, digest
if digest == oldhash: if digest == oldhash:
return None, oldhash # All the chunks for this tile have not changed according to the hash
return True, digest
tileimg = Image.new("RGBA", (width, height)) tileimg = Image.new("RGBA", (width, height))
@@ -364,15 +368,18 @@ def quadtree_recurse(chunkmap, colstart, colend, rowstart, rowend, prefix, quadr
Each tile outputted is always 384 by 384 pixels. Each tile outputted is always 384 by 384 pixels.
The return from this function (path, hash) where path is the path to the The return from this function is (path, hash) where path is the path to the
file saved, and hash is a byte string that depends on the tile's contents. file saved, and hash is a byte string that depends on the tile's contents.
If the tile is blank, path will be None. If the tile is blank, path will be None, but hash will still be valid.
""" """
if 0 and prefix == "/tmp/testrender/2/1/0/1/3" and quadrant == "1": #if 1 and prefix == "/tmp/testrender/2/1/0/1" and quadrant == "1":
print "Called with {0},{1} {2},{3}".format(colstart, colend, rowstart, rowend) # print "Called with {0},{1} {2},{3}".format(colstart, colend, rowstart, rowend)
print " prefix:", prefix # print " prefix:", prefix
print " quadrant:", quadrant # print " quadrant:", quadrant
# dbg = True
#else:
# dbg = False
cols = colend - colstart cols = colend - colstart
rows = rowend - rowstart rows = rowend - rowstart
@@ -384,14 +391,43 @@ def quadtree_recurse(chunkmap, colstart, colend, rowstart, rowend, prefix, quadr
if os.path.exists(hashpath): if os.path.exists(hashpath):
oldhash = open(hashpath, "rb").read() oldhash = open(hashpath, "rb").read()
else: else:
# This method (should) never actually return None for a hash, this is
# used so it will always compare unequal.
oldhash = None oldhash = None
if cols == 2 and rows == 4: if cols == 2 and rows == 4:
# base case: just render the image # base case: just render the image
img, newhash = render_worldtile(chunkmap, colstart, colend, rowstart, rowend, oldhash) img, newhash = render_worldtile(chunkmap, colstart, colend, rowstart, rowend, oldhash)
# There are a few cases to handle here:
# 1) img is None: the image doesn't exist (would have been blank, no
# chunks exist for that range.
# 2) img is True: the image hasn't changed according to the hashes. The
# image object is not returned by render_worldtile, but we do need to
# return the path to it.
# 3) img is a PIL.Image.Image object, a new tile was computed, we need
# to save it and its hash (newhash) to disk.
if not img: if not img:
# Image doesn't exist, exit now # The image returned is blank, there should not be an image here.
# If one does exist, from a previous world or something, it is not
# deleted, but None is returned to indicate to our caller this tile
# is blank.
return None, newhash return None, newhash
if img is True:
# No image was returned because the hashes matched. Return the path
# to the image that already exists and is up to date according to
# the hash
path = os.path.join(prefix, quadrant+".png")
if not os.path.exists(path):
# Oops, the image doesn't actually exist. User must have
# deleted it, or must be some bug?
raise Exception("Error, this image should have existed according to the hashes, but didn't")
return path, newhash
# If img was not None or True, it is an image object. The image exists
# and the hashes did not match, so it must have changed. Fall through
# to the last part of this function which saves the image and its hash.
assert isinstance(img, Image.Image)
elif cols < 2 or rows < 4: elif cols < 2 or rows < 4:
raise Exception("Something went wrong, this tile is too small. (Please send " raise Exception("Something went wrong, this tile is too small. (Please send "
"me the traceback so I can fix this)") "me the traceback so I can fix this)")
@@ -438,15 +474,20 @@ def quadtree_recurse(chunkmap, colstart, colend, rowstart, rowend, prefix, quadr
colmid, colend, rowmid, rowend, colmid, colend, rowmid, rowend,
newprefix, "3") newprefix, "3")
# Is this tile blank? If so, it doesn't matter what the old hash was, #if dbg:
# we can exit right now. # print quad0file
# Note for the confused: python's True value is a subclass of int and # print repr(hash0)
# has value 1, so I can do this: # print quad1file
if (bool(quad0file) + bool(quad1file) + bool(quad2file) + # print repr(hash1)
bool(quad3file)) == 0: # print quad2file
return None, hasher.digest() # print repr(hash2)
# print quad3file
# print repr(hash3)
# Check the hashes. # Check the hashes. This is checked even if the tile files returned
# None, since that could happen if either the tile was blank or it
# hasn't changed. So the hashes returned should tell us whether we need
# to update this tile or not.
hasher.update(hash0) hasher.update(hash0)
hasher.update(hash1) hasher.update(hash1)
hasher.update(hash2) hasher.update(hash2)
@@ -454,8 +495,19 @@ def quadtree_recurse(chunkmap, colstart, colend, rowstart, rowend, prefix, quadr
newhash = hasher.digest() newhash = hasher.digest()
if newhash == oldhash: if newhash == oldhash:
# Nothing left to do, this tile already exists and hasn't changed. # Nothing left to do, this tile already exists and hasn't changed.
#if dbg: print "hashes match, nothing to do"
return os.path.join(prefix, quadrant+".png"), oldhash return os.path.join(prefix, quadrant+".png"), oldhash
# Check here if this tile is actually blank. If all 4 returned quadrant
# filenames are None, this tile should not be rendered. However, we
# still need to return a valid hash for it, so that's why this check is
# below the hash check.
# For the confused: Python boolean values are a subclass of integers,
# and True has value 1, so I can do this:
if (bool(quad0file) + bool(quad1file) + bool(quad2file) +
bool(quad3file)) == 0:
return None, newhash
img = Image.new("RGBA", (384, 384)) img = Image.new("RGBA", (384, 384))
if quad0file: if quad0file: