Page MenuHome

auto_tile_size_release_05.py

auto_tile_size_release_05.py

# BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# END GPL LICENSE BLOCK #####
bl_info = {
"name": "Auto Tile Size",
"description": "Estimate the render tile size that will render the fastest",
"author": "Greg Zaal",
"version": (2, 5),
"blender": (2, 72, 0),
"location": "Render Settings > Performance",
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php?title=Extensions:2.6/Py/Scripts/Render/Auto_Tile_Size",
"tracker_url": "https://projects.blender.org/tracker/index.php?func=detail&aid=36785&group_id=153&atid=467",
"category": "Render"}
import bpy
from bpy.app.handlers import persistent
from math import ceil, floor
class AutoTileSizeSettings(bpy.types.PropertyGroup):
gpu_choice = bpy.props.EnumProperty(
name="Target GPU Tile Size",
items=(("16", "16", "16 x 16"),
("32", "32", "32 x 32"),
("64", "64", "64 x 64"),
("128", "128", "128 x 128"),
("256", "256", "256 x 256"),
("512", "512", "512 x 512"),
("1024", "1024", "1024 x 1024")),
default='256',
description="Square dimentions of tiles",
update=_update_tile_size)
cpu_choice = bpy.props.EnumProperty(
name="Target CPU Tile Size",
items=(("16", "16", "16 x 16"),
("32", "32", "32 x 32"),
("64", "64", "64 x 64"),
("128", "128", "128 x 128"),
("256", "256", "256 x 256"),
("512", "512", "512 x 512"),
("1024", "1024", "1024 x 1024")),
default='32',
description="Square dimentions of tiles",
update=_update_tile_size)
bi_choice = bpy.props.EnumProperty(
name="Target CPU Tile Size",
items=(("16", "16", "16 x 16"),
("32", "32", "32 x 32"),
("64", "64", "64 x 64"),
("128", "128", "128 x 128"),
("256", "256", "256 x 256"),
("512", "512", "512 x 512"),
("1024", "1024", "1024 x 1024")),
default='64',
description="Square dimentions of tiles",
update=_update_tile_size)
optimal = bpy.props.EnumProperty(
name="Optimal Tiles",
default="optimal",
description="Match the target size exactly, or try to find a similar tile size for best performance",
items=(("optimal", "Optimal", "All tiles will be the same size and roughly match the target (usually renders faster)"),
("exact", "Exact", "Most tiles will match the target, but tiles around the edges may be rectangular (renders slower)")),
update=_update_tile_size)
enable = bpy.props.BoolProperty(
name="Auto Tile Size",
default=True,
description="Calculate the best tile size based on factors of the render size and the chosen target",
update=_update_tile_size)
advanced_ui = bpy.props.BoolProperty(
name="Advanced Settings",
default=False,
description="Show extra options for more control over the calculated tile size")
# Internally used props (not for GUI)
first_run = bpy.props.BoolProperty(default=True)
threads_error = bpy.props.BoolProperty()
prev_choice = bpy.props.StringProperty(default='0')
prev_renderer = bpy.props.StringProperty(default='prev_renderer')
prev_device = bpy.props.StringProperty(default='prev_device')
prev_res = bpy.props.StringProperty(default='prev_res')
prev_border = bpy.props.BoolProperty(default=False)
prev_border_res = bpy.props.StringProperty(default="prevborderres")
prev_actual_tile_size = bpy.props.StringProperty(default='prevactual')
prev_threads = bpy.props.IntProperty(default=0)
@persistent
def on_scene_update(context):
scene = bpy.context.scene
settings = scene.ats_settings
if scene.render.engine not in ['CYCLES', 'BLENDER_RENDER'] or not settings.enable:
return False # avoid any of these checks if ATS is disabled or scene uses unsupported renderer
renderer = scene.render.engine
device = scene.cycles.device
border = scene.render.use_border
threads = scene.render.threads
choice = 0
if (device == 'GPU' and bpy.context.user_preferences.system.compute_device_type != 'NONE') and scene.render.engine == 'CYCLES':
choice = settings.gpu_choice
elif (device == 'CPU' or bpy.context.user_preferences.system.compute_device_type == 'NONE') and scene.render.engine == 'CYCLES':
choice = settings.cpu_choice
elif scene.render.engine == 'BLENDER_RENDER':
choice = settings.bi_choice
res = str(get_actual_res('x'))+'x'+str(get_actual_res('y'))
actual_ts = str(scene.render.tile_x)+'x'+str(scene.render.tile_y)
border_res = (str(scene.render.border_min_x)+"x"+str(scene.render.border_min_y))+'-'+(str(scene.render.border_max_x)+"x"+str(scene.render.border_max_y))
# detect relevant changes in scene
change_triggers = [renderer != settings.prev_renderer,
device != settings.prev_device,
border != settings.prev_border,
threads != settings.prev_threads,
choice != settings.prev_choice,
res != settings.prev_res,
border_res != settings.prev_border_res,
actual_ts != settings.prev_actual_tile_size]
if any(change_triggers):
settings.prev_renderer = renderer
settings.prev_device = device
settings.prev_border = border
settings.prev_threads = threads
settings.prev_choice = choice
settings.prev_border_res = border_res
do_set_tile_size(context)
else:
pass
return True
def get_actual_res(xy):
rend = bpy.context.scene.render
rend_percent = rend.resolution_percentage * 0.01
x = int(floor(rend.resolution_x * rend_percent))
y = int(floor(rend.resolution_y * rend_percent))
return x if xy == 'x' else y
def do_set_tile_size(context):
context = bpy.context
scene = context.scene
settings = scene.ats_settings
if scene.render.engine not in ['CYCLES', 'BLENDER_RENDER']:
print("Auto Tile Size is not supported for this renderer")
return False
settings.first_run = False
device = scene.cycles.device
target = 0
xres = get_actual_res('x')
yres = get_actual_res('y')
realxres = xres
realyres = yres
if context.scene.render.use_border:
xres = round(xres * (context.scene.render.border_max_x - context.scene.render.border_min_x))
yres = round(yres * (context.scene.render.border_max_y - context.scene.render.border_min_y))
if (device == 'GPU' and context.user_preferences.system.compute_device_type != 'NONE') and scene.render.engine == 'CYCLES':
target = int(settings.gpu_choice)
elif (device == 'CPU' or context.user_preferences.system.compute_device_type == 'NONE') and scene.render.engine == 'CYCLES':
target = int(settings.cpu_choice)
elif scene.render.engine == 'BLENDER_RENDER':
target = int(settings.bi_choice)
else:
print ("Failed to get compute device")
return False
settings.prev_choice = str(target)
settings.prev_device = device
numtiles_x = ceil(xres / target)
numtiles_y = ceil(yres / target)
if settings.optimal == "optimal":
tile_x = ceil(xres / numtiles_x)
tile_y = ceil(yres / numtiles_y)
print ("Tile size: " + str(tile_x) + "x" + str(tile_y) + " (" + str(ceil(xres / tile_x)) + "x" + str(ceil(yres / tile_y)) + " tiles)")
else:
tile_x = target
tile_y = target
print ("Tile size: " + str(tile_x) + "x" + str(tile_y) + " (" + str(xres / tile_x) + " x " + str(yres / tile_y) + " tiles)")
# Detect if there are fewer tiles than available threads
if ((ceil(numtiles_x) * ceil(numtiles_y)) < scene.render.threads) and not ((device == 'GPU' and context.user_preferences.system.compute_device_type != 'NONE') and scene.render.engine == 'CYCLES'):
settings.threads_error = True
else:
settings.threads_error = False
scene.render.tile_x = tile_x
scene.render.tile_y = tile_y
settings.prev_res = str(realxres)+'x'+str(realyres)
settings.prev_actual_tile_size = str(scene.render.tile_x)+'x'+str(scene.render.tile_y)
return True
class SetTileSize(bpy.types.Operator):
'The first render may not obey the tile-size set here'
bl_idname = 'autotile.set'
bl_label = 'Set'
def execute(self, context):
context.scene.ats_settings.first_run = False
if do_set_tile_size(context):
return {'FINISHED'}
else:
return {'CANCELLED'}
'''
INFERFACE
'''
def ui_layout(renderer, layout, context):
scene = context.scene
settings = scene.ats_settings
col = layout.column(align=True)
row = col.row(align=True)
row.prop(settings, 'enable', toggle=True)
row.prop(settings, 'advanced_ui', toggle=True, text = '', icon = 'PREFERENCES')
if settings.advanced_ui:
col.label("Target tile size:")
row = col.row(align=True)
if renderer == 'CYCLES':
if scene.cycles.device == 'GPU' and context.user_preferences.system.compute_device_type != 'NONE' and scene.render.engine == 'CYCLES':
row.prop(settings, 'gpu_choice', expand=True)
elif scene.cycles.device == 'CPU' or context.user_preferences.system.compute_device_type == 'NONE' and scene.render.engine == 'CYCLES':
row.prop(settings, 'cpu_choice', expand=True)
elif renderer == 'BLENDER_RENDER':
row.prop(settings, 'bi_choice', expand=True)
row = col.row(align=True)
row.prop(settings, 'optimal', text='Consistent Tiles', expand=True)
if settings.first_run:
col = layout.column(align=True)
col.operator('autotile.set', text="First-render fix", icon='ERROR')
elif settings.prev_device != scene.cycles.device:
col = layout.column(align=True)
col.operator('autotile.set', text="Device changed - fix", icon='ERROR')
if (scene.render.tile_x/scene.render.tile_y > 2) or (scene.render.tile_x/scene.render.tile_y < 0.5): # if not very square tile
col.label (text="Warning: Tile size is not very square", icon='ERROR')
col.label (text=" Try a slightly different resolution")
col.label (text=" or choose \"Exact\" above")
if settings.threads_error:
col.label (text="Warning: Fewer tiles than render threads", icon='ERROR')
def menu_func_cycles(self, context):
layout = self.layout
ui_layout('CYCLES', layout, context)
def menu_func_bi(self, context):
layout = self.layout
ui_layout('BLENDER_RENDER', layout, context)
def _update_tile_size(self, context):
do_set_tile_size(context)
'''
REGISTRATION
'''
def register():
bpy.types.CyclesRender_PT_performance.append(menu_func_cycles) # Note, the Cycles addon must be registered first, otherwise this panel doesn't exist
bpy.types.RENDER_PT_performance.append(menu_func_bi)
bpy.app.handlers.scene_update_post.append(on_scene_update)
bpy.utils.register_module(__name__)
bpy.types.Scene.ats_settings = bpy.props.PointerProperty(type=AutoTileSizeSettings)
def unregister():
del bpy.types.Scene.ats_settings
bpy.types.CyclesRender_PT_performance.remove(menu_func_cycles)
bpy.types.RENDER_PT_performance.remove(menu_func_bi)
bpy.app.handlers.scene_update_post.remove(on_scene_update)
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

File Metadata

Mime Type
text/x-python
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
14/aa/fe1b9cc37c0bdb64696003c305ae

Event Timeline