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:
-rw-r--r--render_auto_tile_size.py160
1 files changed, 132 insertions, 28 deletions
diff --git a/render_auto_tile_size.py b/render_auto_tile_size.py
index 8ece451d..a773281d 100644
--- a/render_auto_tile_size.py
+++ b/render_auto_tile_size.py
@@ -20,8 +20,8 @@ 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),
+ "version": (3, 0),
+ "blender": (2, 74, 0),
"location": "Render Settings > Performance",
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php?title=Extensions:2.6/Py/Scripts/Render/Auto_Tile_Size",
@@ -31,7 +31,7 @@ bl_info = {
import bpy
from bpy.app.handlers import persistent
-from math import ceil, floor
+from math import ceil, floor, sqrt
SUPPORTED_RENDER_ENGINES = {'CYCLES', 'BLENDER_RENDER'}
@@ -55,16 +55,14 @@ class AutoTileSizeSettings(bpy.types.PropertyGroup):
name="Target GPU Tile Size",
items=TILE_SIZES,
default='256',
- description="Square dimentions of tiles",
+ description="Square dimentions of tiles for GPU rendering",
update=_update_tile_size)
-
cpu_choice = bpy.props.EnumProperty(
name="Target CPU Tile Size",
items=TILE_SIZES,
default='32',
- description="Square dimentions of tiles",
+ description="Square dimentions of tiles for CPU rendering",
update=_update_tile_size)
-
bi_choice = bpy.props.EnumProperty(
name="Target CPU Tile Size",
items=TILE_SIZES,
@@ -72,6 +70,37 @@ class AutoTileSizeSettings(bpy.types.PropertyGroup):
description="Square dimentions of tiles",
update=_update_tile_size)
+ gpu_custom = bpy.props.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 = bpy.props.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 = bpy.props.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 = bpy.props.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 = bpy.props.BoolProperty(
name="Optimal Tiles",
default=True,
@@ -89,9 +118,16 @@ class AutoTileSizeSettings(bpy.types.PropertyGroup):
default=False,
description="Show extra options for more control over the calculated tile size")
+ thread_error_correct = bpy.props.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 = bpy.props.BoolProperty(default=True, options={'HIDDEN'})
threads_error = bpy.props.BoolProperty(options={'HIDDEN'})
+ num_tiles = bpy.props.IntVectorProperty(default=(0, 0), size=2, 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'})
@@ -109,16 +145,17 @@ def ats_poll(context):
return True
-def ats_get_engine_is_gpu(engine, device, userpref):
+def 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"
+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_choice"
- return "bi_choice"
+ return ("cpu" + target_type)
+ return ("bi" + target_type)
@persistent
@@ -137,9 +174,9 @@ def on_scene_update(scene):
# 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
+ threads = get_threads(context, device)
- choice = getattr(settings, ats_get_tilesize_prop(engine, device, userpref))
+ choice = getattr(settings, get_tilesize_prop(engine, device, userpref))
res = get_actual_res(render)
actual_ts = (render.tile_x, render.tile_y)
@@ -150,7 +187,7 @@ def on_scene_update(scene):
device != settings.prev_device or
border != settings.prev_border or
threads != settings.prev_threads or
- choice != settings.prev_choice 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[:])
@@ -163,6 +200,61 @@ def get_actual_res(render):
# 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.user_preferences
+
+ if engine_is_gpu(engine, device, userpref):
+ gpu_device_str = userpref.system.compute_device
+ if 'MULTI' in gpu_device_str:
+ threads = int(gpu_device_str.split('_')[-1])
+ else:
+ threads = 1
+ 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)
+ if secondary.is_integer(): # will only be an integer if there are the right number of tiles
+ 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):
@@ -176,7 +268,6 @@ def do_set_tile_size(context):
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)
@@ -184,11 +275,12 @@ def do_set_tile_size(context):
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))
+ 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)
@@ -198,20 +290,25 @@ def do_set_tile_size(context):
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):
+ 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
+ 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 = choice
+ 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)
@@ -252,16 +349,21 @@ def ui_layout(engine, layout, context):
row.prop(settings, "is_enabled", toggle=True)
row.prop(settings, "use_advanced_ui", toggle=True, text="", icon='PREFERENCES')
- sub = col.column(align=True)
+ sub = col.column(align=False)
sub.enabled = settings.is_enabled
if settings.use_advanced_ui:
- sub.label("Target tile size:")
+ row = sub.row(align=True)
+ row.label("Target tile size:")
+ row.separator()
+ row.prop(settings, "target_type", expand=True)
row = sub.row(align=True)
- row.prop(settings, ats_get_tilesize_prop(engine, device, userpref), expand=True)
+ row.prop(settings, get_tilesize_prop(engine, device, userpref), expand=True)
sub.prop(settings, "use_optimal", text="Calculate Optimal Size")
+ sub.label("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')
@@ -272,9 +374,11 @@ def ui_layout(engine, layout, context):
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')
+ 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):