diff options
author | Brecht Van Lommel <brecht@blender.org> | 2021-09-23 18:49:59 +0300 |
---|---|---|
committer | Brecht Van Lommel <brecht@blender.org> | 2021-09-23 18:56:24 +0300 |
commit | 20cbcd98e0455a0afdf8c69d7864f0721c49bca0 (patch) | |
tree | 40f8a56102f64328ab4f138f1ecc72fd076533fb | |
parent | c0942837ab5a48482b1e901712f24cb1e1f04fac (diff) |
Auto Tile Size: remove the add-on after the cycles-x merge
The new render scheduling in Cycles makes this obsolete. There can still be
reasons for tweaking tile size, mainly related to memory usage, but the add-on
is not designed for that.
Fixes T91645
-rw-r--r-- | render_auto_tile_size.py | 500 |
1 files changed, 0 insertions, 500 deletions
diff --git a/render_auto_tile_size.py b/render_auto_tile_size.py deleted file mode 100644 index 078513c6..00000000 --- a/render_auto_tile_size.py +++ /dev/null @@ -1,500 +0,0 @@ -# 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": (3, 1, 3), - "blender": (2, 80, 0), - "location": "Render Settings > Performance", - "warning": "", - "doc_url": "{BLENDER_MANUAL_URL}/addons/render/auto_tile_size.html", - "category": "Render", -} - - -import bpy -from bpy.types import ( - Operator, - PropertyGroup, - ) -from bpy.props import ( - BoolProperty, - EnumProperty, - FloatVectorProperty, - IntProperty, - IntVectorProperty, - StringProperty, - PointerProperty, - ) -from bpy.app.handlers import persistent -from math import ( - ceil, floor, - sqrt, - ) - - -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(PropertyGroup): - gpu_choice: EnumProperty( - name="Target GPU Tile Size", - items=TILE_SIZES, - default='256', - description="Square dimensions of tiles for GPU rendering", - update=_update_tile_size - ) - cpu_choice: EnumProperty( - name="Target CPU Tile Size", - items=TILE_SIZES, - default='32', - description="Square dimensions of tiles for CPU rendering", - update=_update_tile_size - ) - bi_choice: EnumProperty( - name="Target CPU Tile Size", - items=TILE_SIZES, - default='64', - description="Square dimensions of tiles", - update=_update_tile_size - ) - gpu_custom: IntProperty( - name="Target Size", - default=256, - min=8, # same as blender's own limits - max=65536, - description="Custom target tile size for GPU rendering", - update=_update_tile_size - ) - cpu_custom: IntProperty( - name="Target Size", - default=32, - min=8, # same as blender's own limits - max=65536, - description="Custom target tile size for CPU rendering", - update=_update_tile_size - ) - bi_custom: IntProperty( - name="Target Size", - default=64, - min=8, # same as blender's own limits - max=65536, - description="Custom target tile size", - update=_update_tile_size - ) - target_type: EnumProperty( - name="Target tile size", - items=( - ('po2', "Po2", "A choice between powers of 2 (16, 32, 64...)"), - ('custom', "Custom", "Choose any number as the tile size target")), - default='po2', - description="Method of choosing the target tile size", - update=_update_tile_size - ) - use_optimal: 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: 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: BoolProperty( - name="Advanced Settings", - default=False, - description="Show extra options for more control over the calculated tile size" - ) - thread_error_correct: BoolProperty( - name="Fix", - default=True, - description="Reduce the tile size so that all your available threads are used", - update=_update_tile_size - ) - - # Internally used props (not for GUI) - first_run: BoolProperty( - default=True, - options={'HIDDEN'} - ) - threads_error: BoolProperty( - options={'HIDDEN'} - ) - num_tiles: IntVectorProperty( - default=(0, 0), - size=2, - options={'HIDDEN'} - ) - prev_choice: StringProperty( - default='', - options={'HIDDEN'} - ) - prev_engine: StringProperty( - default='', - options={'HIDDEN'} - ) - prev_device: StringProperty( - default='', - options={'HIDDEN'} - ) - prev_res: IntVectorProperty( - default=(0, 0), - size=2, - options={'HIDDEN'} - ) - prev_border: BoolProperty( - default=False, - options={'HIDDEN'} - ) - prev_border_res: FloatVectorProperty( - default=(0, 0, 0, 0), - size=4, - options={'HIDDEN'} - ) - prev_actual_tile_size: IntVectorProperty( - default=(0, 0), - size=2, - options={'HIDDEN'} - ) - prev_threads: 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 engine_is_gpu(engine, device, userpref): - if engine == 'CYCLES' and device == 'GPU': - return userpref.addons['cycles'].preferences.has_active_device() - return False - - -def get_tilesize_prop(engine, device, userpref): - target_type = "_choice" if bpy.context.scene.ats_settings.target_type == 'po2' else "_custom" - if engine_is_gpu(engine, device, userpref): - return ("gpu" + target_type) - elif engine == 'CYCLES': - return ("cpu" + target_type) - return ("bi" + target_type) - - -@persistent -def on_scene_update(scene): - context = bpy.context - - if not ats_poll(context): - return - - userpref = context.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 = get_threads(context, device) - - choice = getattr(settings, 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 - str(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 get_threads(context, device): - render = context.scene.render - engine = render.engine - userpref = context.preferences - - if engine_is_gpu(engine, device, userpref): - threads = userpref.addons['cycles'].preferences.get_num_gpu_devices() - else: - threads = render.threads - - return threads - - -def max_tile_size(threads, xres, yres): - ''' Give the largest tile size that will still use all threads ''' - - render_area = xres * yres - tile_area = render_area / threads - tile_length = sqrt(tile_area) - - # lists: num x tiles, num y tiles, squareness, total tiles - perfect_attempts = [] # attempts with correct number of tiles - attempts = [] # all attempts, even if incorrect number of tiles - - axes = [xres, yres] - funcs = [floor, ceil] - - for axis in axes: - sec_axis = yres if axis == xres else xres - for func in funcs: - primary = func(axis / tile_length) - if primary > 0: - secondary = threads / primary - ts_p = axis / primary - ts_s = sec_axis / secondary - squareness = max(ts_p, ts_s) - min(ts_p, ts_s) - attempt = [primary if axis == xres else secondary, primary if - axis != xres else secondary, squareness, primary * secondary] - if attempt not in attempts: - attempts.append(attempt) - # will only be an integer if there are the right number of tiles - if secondary.is_integer(): - perfect_attempts.append(attempt) - - if perfect_attempts: # prefer to use attempt that has exactly the right number of tiles - attempts = perfect_attempts - - attempt = sorted(attempts, key=lambda k: k[2])[0] # pick set with most square tiles - numtiles_x = round(attempt[0]) - numtiles_y = round(attempt[1]) - tile_x = ceil(xres / numtiles_x) - tile_y = ceil(yres / numtiles_y) - - return (tile_x, tile_y) - - -def do_set_tile_size(context): - if not ats_poll(context): - return False - - scene = context.scene - userpref = context.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 - - 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, get_tilesize_prop(engine, device, userpref)) - target = int(choice) - - numtiles_x = ceil(xres / target) - numtiles_y = ceil(yres / target) - settings.num_tiles = (numtiles_x, numtiles_y) - 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 (for debug purposes) - # print("Tile size: %dx%d (%dx%d tiles)" % (tile_x, tile_y, ceil(xres / tile_x), ceil(yres / tile_y))) - - # Detect if there are fewer tiles than available threads - threads = get_threads(context, device) - if ((numtiles_x * numtiles_y) < threads): - settings.threads_error = True - if settings.thread_error_correct: - tile_x, tile_y = max_tile_size(threads, xres, yres) - settings.num_tiles = (ceil(xres / tile_x), ceil(yres / tile_y)) - else: - settings.threads_error = False - - # Make sure tile sizes are within the internal limit - tile_x = max(8, tile_x) - tile_y = max(8, tile_y) - tile_x = min(65536, tile_x) - tile_y = min(65536, tile_y) - - render.tile_x = tile_x - render.tile_y = tile_y - - settings.prev_engine = engine - settings.prev_device = device - settings.prev_border = border - settings.prev_threads = threads - settings.prev_choice = str(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(Operator): - bl_idname = "render.autotilesize_set" - bl_label = "Set" - bl_description = "The first render may not obey the tile-size set here" - - @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.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=False) - sub.enabled = settings.is_enabled - - if settings.use_advanced_ui: - row = sub.row(align=True) - row.label(text="Target tile size:") - row.separator() - row.prop(settings, "target_type", expand=True) - - row = sub.row(align=True) - row.prop(settings, get_tilesize_prop(engine, device, userpref), expand=True) - sub.prop(settings, "use_optimal", text="Calculate Optimal Size") - - sub.label(text="Number of tiles: %s x %s (Total: %s)" % - (settings.num_tiles[0], settings.num_tiles[1], - settings.num_tiles[0] * settings.num_tiles[1]) - ) - - 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 not very square tile - if (render.tile_x / render.tile_y > 2) or (render.tile_x / render.tile_y < 0.5): - sub.label(text="Warning: Tile size is not very square", icon='ERROR') - sub.label(text=" Try a slightly different resolution") - - if settings.threads_error: - row = sub.row(align=True) - row.alignment = 'CENTER' - row.label(text="Warning: Fewer tiles than threads", icon='ERROR') - row.prop(settings, 'thread_error_correct') - - -def menu_func_cycles(self, context): - ui_layout('CYCLES', self.layout, context) - - -# ##### REGISTRATION ##### - -classes = ( - AutoTileSizeSettings, - SetTileSize -) - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - - bpy.types.Scene.ats_settings = 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, "CYCLES_RENDER_PT_performance", None) - if cycles_panel is not None: - cycles_panel.append(menu_func_cycles) - - bpy.app.handlers.depsgraph_update_post.append(on_scene_update) - - -def unregister(): - bpy.app.handlers.depsgraph_update_post.remove(on_scene_update) - - cycles_panel = getattr(bpy.types, "CYCLES_RENDER_PT_performance", None) - if cycles_panel is not None: - cycles_panel.remove(menu_func_cycles) - - del bpy.types.Scene.ats_settings - - for cls in reversed(classes): - bpy.utils.unregister_class(cls) - - -if __name__ == "__main__": - register() |