Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Montagne <montagne29@wanadoo.fr>2015-01-13 23:43:01 +0300
committerBastien Montagne <montagne29@wanadoo.fr>2015-01-13 23:43:01 +0300
commita32177f76b250183612ac2f38ec29157b4f88c1d (patch)
tree1b54f9aaa632547e6577e97519ede3ac39a0549b /render_auto_tile_size.py
parentb8577cee6fd9aa8483ce66e79df0cdfd6542f403 (diff)
Move 'render auto tile size' addon to main repo.
Diffstat (limited to 'render_auto_tile_size.py')
-rw-r--r--render_auto_tile_size.py318
1 files changed, 318 insertions, 0 deletions
diff --git a/render_auto_tile_size.py b/render_auto_tile_size.py
new file mode 100644
index 00000000..8ece451d
--- /dev/null
+++ b/render_auto_tile_size.py
@@ -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()