Page Menu
Home
Search
Configure Global Search
Log In
Files
F120999
auto_tile_size_release_08.py
Greg Zaal (gregzaal)
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Authored By
Greg Zaal (gregzaal)
Nov 1 2014, 8:35 AM
Size
10 KB
Subscribers
None
auto_tile_size_release_08.py
View Options
# 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
)
# XXX There is no need for this to be an enum, a mere bool should be enough?
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:
%d
x
%d
(
%d
x
%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
()
File Metadata
Details
Attached
Mime Type
text/x-python
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
6e/13/e52434c214786cbfeb1a52192ea0
Attached To
T36785: Automatically calculate the best tile size for the fastest render
Event Timeline
Log In to Comment