moved comments and added unimplemented methods.
I think I'm going to erase them and go a different direction with this implemention though. Creating a commit here incase I change my mind.
This commit is contained in:
@@ -20,6 +20,7 @@ import os.path
|
|||||||
import shutil
|
import shutil
|
||||||
import random
|
import random
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
import threading, Queue
|
||||||
|
|
||||||
from .util import iterate_base4, convert_coords
|
from .util import iterate_base4, convert_coords
|
||||||
|
|
||||||
@@ -78,6 +79,81 @@ do_work(workobj)
|
|||||||
# world
|
# world
|
||||||
Bounds = namedtuple("Bounds", ("mincol", "maxcol", "minrow", "maxrow"))
|
Bounds = namedtuple("Bounds", ("mincol", "maxcol", "minrow", "maxrow"))
|
||||||
|
|
||||||
|
# A note about the implementation of the different rendercheck modes:
|
||||||
|
#
|
||||||
|
# For reference, here's what the rendercheck modes are:
|
||||||
|
# 0
|
||||||
|
# Only render tiles that have chunks with a greater mtime than
|
||||||
|
# the last render timestamp, and their ancestors.
|
||||||
|
#
|
||||||
|
# In other words, only renders parts of the map that have changed
|
||||||
|
# since last render, nothing more, nothing less.
|
||||||
|
#
|
||||||
|
# This is the fastest option, but will not detect tiles that have
|
||||||
|
# e.g. been deleted from the directory tree, or pick up where a
|
||||||
|
# partial interrupted render left off.
|
||||||
|
|
||||||
|
# 1
|
||||||
|
# For render-tiles, render all whose chunks have an mtime greater
|
||||||
|
# than the mtime of the tile on disk, and their upper-tile
|
||||||
|
# ancestors.
|
||||||
|
#
|
||||||
|
# Also check all other upper-tiles and render any that have
|
||||||
|
# children with more rencent mtimes than itself.
|
||||||
|
#
|
||||||
|
# This is slower due to stat calls to determine tile mtimes, but
|
||||||
|
# safe if the last render was interrupted.
|
||||||
|
|
||||||
|
# 2
|
||||||
|
# Render all tiles unconditionally. This is a "forcerender" and
|
||||||
|
# is the slowest, but SHOULD be specified if this is the first
|
||||||
|
# render because the scan will forgo tile stat calls. It's also
|
||||||
|
# useful for changing texture packs or other options that effect
|
||||||
|
# the output.
|
||||||
|
#
|
||||||
|
# For 0 our caller has explicitly requested not to check mtimes on
|
||||||
|
# disk, to speed things up. So the mode 0 chunk scan only looks at
|
||||||
|
# chunk mtimes and the last render mtime, and has marked only the
|
||||||
|
# render-tiles that need rendering. Mode 0 then iterates over all dirty
|
||||||
|
# render-tiles and upper-tiles that depend on them. It does not check
|
||||||
|
# mtimes of upper-tiles, so this is only a good option if the last
|
||||||
|
# render was not interrupted.
|
||||||
|
|
||||||
|
# For mode 2, this is a forcerender, the caller has requested we render
|
||||||
|
# everything. The mode 2 chunk scan marks every tile as needing
|
||||||
|
# rendering, and disregards mtimes completely. Mode 2 then iterates
|
||||||
|
# over all render-tiles and upper-tiles that depend on them, which is
|
||||||
|
# every tile that should exist.
|
||||||
|
|
||||||
|
# In both 0 and 2 the render iteration is the same: the dirtytile tree
|
||||||
|
# built is authoritive on every tile that needs rendering.
|
||||||
|
|
||||||
|
# In mode 1, things are most complicated. The mode 2 chunk scan checks
|
||||||
|
# every render tile's mtime for each chunk that touches it, so it can
|
||||||
|
# determine accurately which tiles need rendering regardless of the
|
||||||
|
# state on disk. The chunk scan also builds a RendertileSet of *every*
|
||||||
|
# render-tile that exists.
|
||||||
|
|
||||||
|
# The mode 1 render iteration then manually iterates over the set of
|
||||||
|
# all render-tiles in a post-traversal order. When it visits a
|
||||||
|
# render-node, it does the following:
|
||||||
|
# * Checks the set of dirty render-tiles to see if the node needs
|
||||||
|
# rendering, and if so, renders it
|
||||||
|
# * If the tile was rendered, set the mtime using os.utime() to the max
|
||||||
|
# of the chunk mtimes.
|
||||||
|
# * If the tile was rendered, return (True, mtime).
|
||||||
|
# * If the tile was not rendered, return (False, mtime)
|
||||||
|
#
|
||||||
|
# Then, for upper-tiles, it does the following:
|
||||||
|
# * Gathers the return values of each child call.
|
||||||
|
# * If any child returned True, render this tile.
|
||||||
|
# * Otherwise, check this tile's mtime. If any child's mtime is greater
|
||||||
|
# than this tile's mtime, render this tile.
|
||||||
|
# * If the tile was rendered, set the mtime using os.utime() to the max
|
||||||
|
# of the child mtimes.
|
||||||
|
# * If the tile was rendered, return (True, mtime).
|
||||||
|
# * If the tile was not rendered, return (False, mtime)
|
||||||
|
|
||||||
__all__ = ["TileSet"]
|
__all__ = ["TileSet"]
|
||||||
class TileSet(object):
|
class TileSet(object):
|
||||||
"""The TileSet object manages the work required to produce a set of tiles
|
"""The TileSet object manages the work required to produce a set of tiles
|
||||||
@@ -247,53 +323,38 @@ class TileSet(object):
|
|||||||
This method returns an iterator over (obj, [dependencies, ...])
|
This method returns an iterator over (obj, [dependencies, ...])
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# With renderchecks set to 0 or 2, simply iterate over the dirty tiles
|
# See note at the top of this file about the rendercheck modes for an
|
||||||
# tree in post-traversal order and yield each tile path.
|
# explanation of what this method does in different situations.
|
||||||
|
|
||||||
# For 0 our caller has explicitly requested not to check mtimes on
|
|
||||||
# disk, to speed things up. So the mode 0 chunk scan only looks at
|
|
||||||
# chunk mtimes and the last render mtime, and has marked only the
|
|
||||||
# render-tiles that need rendering. Mode 0 then iterates over all dirty
|
|
||||||
# render-tiles and upper-tiles that depend on them. It does not check
|
|
||||||
# mtimes of upper-tiles, so this is only a good option if the last
|
|
||||||
# render was not interrupted.
|
|
||||||
|
|
||||||
# For mode 2, this is a forcerender, the caller has requested we render
|
|
||||||
# everything. The mode 2 chunk scan marks every tile as needing
|
|
||||||
# rendering, and disregards mtimes completely. Mode 2 then iterates
|
|
||||||
# over all render-tiles and upper-tiles that depend on them, which is
|
|
||||||
# every tile that should exist.
|
|
||||||
|
|
||||||
# In both 0 and 2 the render iteration is the same: the dirtytile tree
|
|
||||||
# built is authoritive on every tile that needs rendering.
|
|
||||||
|
|
||||||
# In mode 1, things are most complicated. The mode 2 chunk scan checks
|
|
||||||
# every render tile's mtime for each chunk that touches it, so it can
|
|
||||||
# determine accurately which tiles need rendering regardless of the
|
|
||||||
# state on disk. The chunk scan also builds a RendertileSet of *every*
|
|
||||||
# render-tile that exists.
|
|
||||||
|
|
||||||
# The mode 1 render iteration then manually iterates over the set of
|
|
||||||
# all render-tiles in a post-traversal order. When it visits a
|
|
||||||
# render-node, it does the following:
|
|
||||||
# * Checks the set of dirty render-tiles to see if the node needs
|
|
||||||
# rendering, and if so, renders it
|
|
||||||
# * If the tile was rendered, set the mtime using os.utime() to the max
|
|
||||||
# of the chunk mtimes.
|
|
||||||
# * If the tile was rendered, return (True, mtime).
|
|
||||||
# * If the tile was not rendered, return (False, mtime)
|
|
||||||
#
|
#
|
||||||
# Then, for upper-tiles, it does the following:
|
# For modes 0 and 2, iterate over the tiles in self.dirtytree by using
|
||||||
# * Gathers the return values of each child call.
|
# the posttraversal() method. Yield each item. Easy.
|
||||||
# * If any child returned True, render this tile.
|
#
|
||||||
# * Otherwise, check this tile's mtime. If any child's mtime is greater
|
# For mode 1, invoke a more complex recursive routine
|
||||||
# than this tile's mtime, render this tile.
|
if self.options['renderchecks'] in (0,2):
|
||||||
# * If the tile was rendered, set the mtime using os.utime() to the max
|
for tilepath in self.dirtytree.posttraversal():
|
||||||
# of the child mtimes.
|
dependencies = []
|
||||||
# * If the tile was rendered, return (True, mtime).
|
# These tiles may or may not exist, but the dispatcher won't
|
||||||
# * If the tile was not rendered, return (False, mtime)
|
# care according to the worker interface protocol It will only
|
||||||
|
# wait for the items that do exist and are in the queue.
|
||||||
|
for i in range(4):
|
||||||
|
dependencies.append( "%s/%s" % (tilepath, i) )
|
||||||
|
yield tilepath, dependencies
|
||||||
|
|
||||||
pass
|
else:
|
||||||
|
# I hope this is kosher. I couldn't think of any other good way to
|
||||||
|
# use a complex recursive routine as one big generator/iterator
|
||||||
|
torender = Queue.Queue(1)
|
||||||
|
thread = threading.Thread(target=self._find_dirty_tiles, args=(torender,))
|
||||||
|
thread.start()
|
||||||
|
item = torender.get()
|
||||||
|
while item is not None:
|
||||||
|
dependencies = []
|
||||||
|
for i in range(4):
|
||||||
|
dependencies.append( "%s/%s" % (item, i) )
|
||||||
|
yield item, dependencies
|
||||||
|
|
||||||
|
item = torender.get()
|
||||||
|
|
||||||
|
|
||||||
def do_work(self, tileobj):
|
def do_work(self, tileobj):
|
||||||
"""Renders the given tile.
|
"""Renders the given tile.
|
||||||
@@ -420,14 +481,20 @@ class TileSet(object):
|
|||||||
time of the map
|
time of the map
|
||||||
|
|
||||||
For rendercheck mode 1: compares chunk mtimes against the tile mtimes
|
For rendercheck mode 1: compares chunk mtimes against the tile mtimes
|
||||||
on disk
|
on disk, and also builds a tileset of every tile
|
||||||
|
|
||||||
For rendercheck mode 2: marks every tile, does not check any mtimes.
|
For rendercheck mode 2: marks every tile, does not check any mtimes.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# See note at the top of this file about the rendercheck modes for an
|
||||||
|
# explanation of what this method does in different situations.
|
||||||
|
|
||||||
depth = self.treedepth
|
depth = self.treedepth
|
||||||
|
|
||||||
dirty = RendertileSet(depth)
|
dirty = RendertileSet(depth)
|
||||||
|
build_fulltileset = self.options['renderchecks'] == 1
|
||||||
|
if build_fulltileset:
|
||||||
|
fulltileset = RendertileSet(depth)
|
||||||
|
|
||||||
chunkcount = 0
|
chunkcount = 0
|
||||||
stime = time.time()
|
stime = time.time()
|
||||||
@@ -507,6 +574,9 @@ class TileSet(object):
|
|||||||
# Computes the path in the quadtree from the col,row coordinates
|
# Computes the path in the quadtree from the col,row coordinates
|
||||||
tile = RenderTile.compute_path(c, r, depth)
|
tile = RenderTile.compute_path(c, r, depth)
|
||||||
|
|
||||||
|
if build_fulltileset:
|
||||||
|
fulltileset.add(tile.path)
|
||||||
|
|
||||||
if rendercheck == 2:
|
if rendercheck == 2:
|
||||||
# Skip all other checks, mark tiles as dirty unconditionally
|
# Skip all other checks, mark tiles as dirty unconditionally
|
||||||
dirty.add(tile.path)
|
dirty.add(tile.path)
|
||||||
@@ -537,11 +607,44 @@ class TileSet(object):
|
|||||||
self, chunkcount, t,
|
self, chunkcount, t,
|
||||||
"s" if t != 1 else "")
|
"s" if t != 1 else "")
|
||||||
|
|
||||||
|
if build_fulltileset:
|
||||||
|
self.fulltileset = fulltileset
|
||||||
return dirty
|
return dirty
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "<TileSet for %s>" % os.basename(self.outputdir)
|
return "<TileSet for %s>" % os.basename(self.outputdir)
|
||||||
|
|
||||||
|
def _find_dirty_tiles(self, renderqueue):
|
||||||
|
"""Entry point for the tile iteration thread. This pushes a number of
|
||||||
|
tile paths onto the renderqueue Queue object, and then pushes a
|
||||||
|
sentinel None and exits
|
||||||
|
|
||||||
|
"""
|
||||||
|
self._find_dirty_tiles_helper(renderqueue, [], self.fulltileset)
|
||||||
|
renderqueue.push(None)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _find_dirty_tiles_helper(self, renderqueue, path, treenode):
|
||||||
|
"""Helper recursion method for _find_dirty_tiles
|
||||||
|
|
||||||
|
This method takes two arguments:
|
||||||
|
* a path, a list of integers. Methods should either not mutate it or
|
||||||
|
make sure it is back as it was when it exits.
|
||||||
|
* treenode: a RendertileSet object corresponding to the node in
|
||||||
|
self.fulltileset corresponding to the above path, or None for
|
||||||
|
rendertiles
|
||||||
|
|
||||||
|
This method returns two things:
|
||||||
|
* A boolean indicating whether this tile was rendered or not
|
||||||
|
* An mtime indicating this tile's mtime. The parent should be rendered
|
||||||
|
if it is older than this value
|
||||||
|
|
||||||
|
For an explanation of what this method does, see the comments at the
|
||||||
|
top of this file.
|
||||||
|
"""
|
||||||
|
if treenode.depth == 1:
|
||||||
|
# This call corresponds to the layer /above/ a render-tile
|
||||||
|
|
||||||
|
|
||||||
def get_dirdepth(outputdir):
|
def get_dirdepth(outputdir):
|
||||||
"""Returns the current depth of the tree on disk
|
"""Returns the current depth of the tree on disk
|
||||||
|
|||||||
Reference in New Issue
Block a user