Move 'render auto tile size' addon to main repo.
This commit is contained in:
parent
b8577cee6f
commit
a32177f76b
|
@ -0,0 +1,318 @@
|
|||
# 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 and set the tile size that will render the fastest",
|
||||
"author": "Greg Zaal",
|
||||
"version": (2, 7),
|
||||
"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",
|
||||
"category": "Render",
|
||||
}
|
||||
|
||||
|
||||
import bpy
|
||||
from bpy.app.handlers import persistent
|
||||
from math import ceil, floor
|
||||
|
||||
|
||||
SUPPORTED_RENDER_ENGINES = {'CYCLES', 'BLENDER_RENDER'}
|
||||
TILE_SIZES = (
|
||||
('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"),
|
||||
)
|
||||
|
||||
|
||||
def _update_tile_size(self, context):
|
||||
do_set_tile_size(context)
|
||||
|
||||
|
||||
class AutoTileSizeSettings(bpy.types.PropertyGroup):
|
||||
gpu_choice = bpy.props.EnumProperty(
|
||||
name="Target GPU Tile Size",
|
||||
items=TILE_SIZES,
|
||||
default='256',
|
||||
description="Square dimentions of tiles",
|
||||
update=_update_tile_size)
|
||||
|
||||
cpu_choice = bpy.props.EnumProperty(
|
||||
name="Target CPU Tile Size",
|
||||
items=TILE_SIZES,
|
||||
default='32',
|
||||
description="Square dimentions of tiles",
|
||||
update=_update_tile_size)
|
||||
|
||||
bi_choice = bpy.props.EnumProperty(
|
||||
name="Target CPU Tile Size",
|
||||
items=TILE_SIZES,
|
||||
default='64',
|
||||
description="Square dimentions of tiles",
|
||||
update=_update_tile_size)
|
||||
|
||||
use_optimal = bpy.props.BoolProperty(
|
||||
name="Optimal Tiles",
|
||||
default=True,
|
||||
description="Try to find a similar tile size for best performance, instead of using exact selected one",
|
||||
update=_update_tile_size)
|
||||
|
||||
is_enabled = 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)
|
||||
|
||||
use_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, options={'HIDDEN'})
|
||||
threads_error = bpy.props.BoolProperty(options={'HIDDEN'})
|
||||
prev_choice = bpy.props.StringProperty(default='', options={'HIDDEN'})
|
||||
prev_engine = bpy.props.StringProperty(default='', options={'HIDDEN'})
|
||||
prev_device = bpy.props.StringProperty(default='', options={'HIDDEN'})
|
||||
prev_res = bpy.props.IntVectorProperty(default=(0, 0), size=2, options={'HIDDEN'})
|
||||
prev_border = bpy.props.BoolProperty(default=False, options={'HIDDEN'})
|
||||
prev_border_res = bpy.props.FloatVectorProperty(default=(0, 0, 0, 0), size=4, options={'HIDDEN'})
|
||||
prev_actual_tile_size = bpy.props.IntVectorProperty(default=(0, 0), size=2, options={'HIDDEN'})
|
||||
prev_threads = bpy.props.IntProperty(default=0, options={'HIDDEN'})
|
||||
|
||||
|
||||
def ats_poll(context):
|
||||
scene = context.scene
|
||||
if scene.render.engine not in SUPPORTED_RENDER_ENGINES or not scene.ats_settings.is_enabled:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def ats_get_engine_is_gpu(engine, device, userpref):
|
||||
return engine == 'CYCLES' and device == 'GPU' and userpref.system.compute_device_type != 'NONE'
|
||||
|
||||
|
||||
def ats_get_tilesize_prop(engine, device, userpref):
|
||||
if ats_get_engine_is_gpu(engine, device, userpref):
|
||||
return "gpu_choice"
|
||||
elif engine == 'CYCLES':
|
||||
return "cpu_choice"
|
||||
return "bi_choice"
|
||||
|
||||
|
||||
@persistent
|
||||
def on_scene_update(scene):
|
||||
context = bpy.context
|
||||
|
||||
if not ats_poll(context):
|
||||
return
|
||||
|
||||
userpref = context.user_preferences
|
||||
|
||||
settings = scene.ats_settings
|
||||
render = scene.render
|
||||
engine = render.engine
|
||||
|
||||
# scene.cycles might not always exist (Cycles is an addon)...
|
||||
device = scene.cycles.device if engine == 'CYCLES' else settings.prev_device
|
||||
border = render.use_border
|
||||
threads = render.threads
|
||||
|
||||
choice = getattr(settings, ats_get_tilesize_prop(engine, device, userpref))
|
||||
|
||||
res = get_actual_res(render)
|
||||
actual_ts = (render.tile_x, render.tile_y)
|
||||
border_res = (render.border_min_x, render.border_min_y, render.border_max_x, render.border_max_y)
|
||||
|
||||
# detect relevant changes in scene
|
||||
do_change = (engine != settings.prev_engine or
|
||||
device != settings.prev_device or
|
||||
border != settings.prev_border or
|
||||
threads != settings.prev_threads or
|
||||
choice != settings.prev_choice or
|
||||
res != settings.prev_res[:] or
|
||||
border_res != settings.prev_border_res[:] or
|
||||
actual_ts != settings.prev_actual_tile_size[:])
|
||||
if do_change:
|
||||
do_set_tile_size(context)
|
||||
|
||||
|
||||
def get_actual_res(render):
|
||||
rend_percent = render.resolution_percentage * 0.01
|
||||
# floor is implicitly done by int conversion...
|
||||
return (int(render.resolution_x * rend_percent), int(render.resolution_y * rend_percent))
|
||||
|
||||
|
||||
def do_set_tile_size(context):
|
||||
if not ats_poll(context):
|
||||
return False
|
||||
|
||||
scene = context.scene
|
||||
userpref = context.user_preferences
|
||||
|
||||
settings = scene.ats_settings
|
||||
render = scene.render
|
||||
engine = render.engine
|
||||
device = scene.cycles.device if engine == 'CYCLES' else settings.prev_device
|
||||
border = render.use_border
|
||||
threads = render.threads
|
||||
|
||||
realxres, realyres = xres, yres = res = get_actual_res(scene.render)
|
||||
|
||||
if border:
|
||||
xres = round(xres * (render.border_max_x - render.border_min_x))
|
||||
yres = round(yres * (render.border_max_y - render.border_min_y))
|
||||
|
||||
choice = getattr(settings, ats_get_tilesize_prop(engine, device, userpref))
|
||||
target = int(choice)
|
||||
|
||||
numtiles_x = ceil(xres / target)
|
||||
numtiles_y = ceil(yres / target)
|
||||
if settings.use_optimal:
|
||||
tile_x = ceil(xres / numtiles_x)
|
||||
tile_y = ceil(yres / numtiles_y)
|
||||
else:
|
||||
tile_x = target
|
||||
tile_y = target
|
||||
|
||||
print("Tile size: %dx%d (%dx%d tiles)" % (tile_x, tile_y, ceil(xres / tile_x), ceil(yres / tile_y)))
|
||||
|
||||
render.tile_x = tile_x
|
||||
render.tile_y = tile_y
|
||||
|
||||
# Detect if there are fewer tiles than available threads
|
||||
if ((numtiles_x * numtiles_y) < threads) and not ats_get_engine_is_gpu(engine, device, userpref):
|
||||
settings.threads_error = True
|
||||
else:
|
||||
settings.threads_error = False
|
||||
|
||||
settings.prev_engine = engine
|
||||
settings.prev_device = device
|
||||
settings.prev_border = border
|
||||
settings.prev_threads = threads
|
||||
settings.prev_choice = choice
|
||||
settings.prev_res = res
|
||||
settings.prev_border_res = (render.border_min_x, render.border_min_y, render.border_max_x, render.border_max_y)
|
||||
settings.prev_actual_tile_size = (tile_x, tile_y)
|
||||
settings.first_run = False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class SetTileSize(bpy.types.Operator):
|
||||
"""The first render may not obey the tile-size set here"""
|
||||
bl_idname = "render.autotilesize_set"
|
||||
bl_label = "Set"
|
||||
|
||||
@classmethod
|
||||
def poll(clss, context):
|
||||
return ats_poll(context)
|
||||
|
||||
def execute(self, context):
|
||||
if do_set_tile_size(context):
|
||||
return {'FINISHED'}
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
# ##### INTERFACE #####
|
||||
|
||||
def ui_layout(engine, layout, context):
|
||||
scene = context.scene
|
||||
userpref = context.user_preferences
|
||||
|
||||
settings = scene.ats_settings
|
||||
render = scene.render
|
||||
engine = render.engine
|
||||
device = scene.cycles.device if engine == 'CYCLES' else settings.prev_device
|
||||
|
||||
col = layout.column(align=True)
|
||||
sub = col.column(align=True)
|
||||
row = sub.row(align=True)
|
||||
row.prop(settings, "is_enabled", toggle=True)
|
||||
row.prop(settings, "use_advanced_ui", toggle=True, text="", icon='PREFERENCES')
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.enabled = settings.is_enabled
|
||||
|
||||
if settings.use_advanced_ui:
|
||||
sub.label("Target tile size:")
|
||||
|
||||
row = sub.row(align=True)
|
||||
row.prop(settings, ats_get_tilesize_prop(engine, device, userpref), expand=True)
|
||||
sub.prop(settings, "use_optimal", text="Calculate Optimal Size")
|
||||
|
||||
if settings.first_run:
|
||||
sub = layout.column(align=True)
|
||||
sub.operator("render.autotilesize_set", text="First-render fix", icon='ERROR')
|
||||
elif settings.prev_device != device:
|
||||
sub = layout.column(align=True)
|
||||
sub.operator("render.autotilesize_set", text="Device changed - fix", icon='ERROR')
|
||||
|
||||
if (render.tile_x / render.tile_y > 2) or (render.tile_x / render.tile_y < 0.5): # if not very square tile
|
||||
sub.label(text="Warning: Tile size is not very square", icon='ERROR')
|
||||
sub.label(text=" Try a slightly different resolution")
|
||||
sub.label(text=" or choose \"Exact\" above")
|
||||
if settings.threads_error:
|
||||
sub.label(text="Warning: Fewer tiles than render threads", icon='ERROR')
|
||||
|
||||
|
||||
def menu_func_cycles(self, context):
|
||||
ui_layout('CYCLES', self.layout, context)
|
||||
|
||||
|
||||
def menu_func_bi(self, context):
|
||||
ui_layout('BLENDER_RENDER', self.layout, context)
|
||||
|
||||
|
||||
# ##### REGISTRATION #####
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
bpy.types.Scene.ats_settings = bpy.props.PointerProperty(type=AutoTileSizeSettings)
|
||||
|
||||
# Note, the Cycles addon must be registered first, otherwise this panel doesn't exist - better be safe here!
|
||||
cycles_panel = getattr(bpy.types, "CyclesRender_PT_performance", None)
|
||||
if cycles_panel is not None:
|
||||
cycles_panel.append(menu_func_cycles)
|
||||
|
||||
bpy.types.RENDER_PT_performance.append(menu_func_bi)
|
||||
bpy.app.handlers.scene_update_post.append(on_scene_update)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.app.handlers.scene_update_post.remove(on_scene_update)
|
||||
bpy.types.RENDER_PT_performance.remove(menu_func_bi)
|
||||
|
||||
cycles_panel = getattr(bpy.types, "CyclesRender_PT_performance", None)
|
||||
if cycles_panel is not None:
|
||||
cycles_panel.remove(menu_func_cycles)
|
||||
|
||||
del bpy.types.Scene.ats_settings
|
||||
|
||||
bpy.utils.unregister_module(__name__)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
Loading…
Reference in New Issue