Profiling buddy
parent
3407bc79e9
commit
78a7d6f900
10
README.md
10
README.md
|
@ -1,7 +1,9 @@
|
|||
# GIT_TEMPLATE
|
||||
> Template for the others repository
|
||||
# PROFILING BUDDY
|
||||
|
||||
Profiling Panel in Modifier stack
|
||||
|
||||
Forked from Simon Thommes [random-blender-scripts](https://gitlab.com/simonthommes/random-blender-scripts/-/tree/master/addons/profiling_buddy?ref_type=heads)
|
||||
|
||||
Git_template is a template repository that must be used to generate others repository
|
||||
|
||||
<!-- TABLE OF CONTENTS -->
|
||||
***
|
||||
|
@ -34,7 +36,7 @@ Git_template is a template repository that must be used to generate others repos
|
|||
```
|
||||
2. Create your own local directory in
|
||||
```sh
|
||||
git clone ssh://git@git.autourdeminuit.com:222/autour_de_minuit/git_template.git
|
||||
git clone ssh://git@git.autourdeminuit.com:222/autour_de_minuit/profiling_buddy.git
|
||||
```
|
||||
|
||||
<!-- CONTENTS -->
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import bpy
|
||||
from . import modifier_profiling
|
||||
|
||||
bl_info = {
|
||||
"name": "Profiling Buddy",
|
||||
"author": "Simon Thommes, Samuel Bernou",
|
||||
"version": (0, 2),
|
||||
"blender": (3, 5, 0),
|
||||
"location": "Properties > Modifier > Profiling Buddy",
|
||||
"description": "Adds panels to profile execution times.",
|
||||
"category": "Workflow",
|
||||
}
|
||||
|
||||
modules = [modifier_profiling]
|
||||
|
||||
def register():
|
||||
for m in modules:
|
||||
m.register()
|
||||
|
||||
def unregister():
|
||||
for m in modules:
|
||||
m.unregister()
|
|
@ -0,0 +1,240 @@
|
|||
import bpy
|
||||
from bpy.types import PropertyGroup, Operator
|
||||
from bpy.props import (StringProperty,
|
||||
FloatProperty,
|
||||
PointerProperty,
|
||||
CollectionProperty)
|
||||
|
||||
class PB_Modifier_Profiling_Panel(bpy.types.Panel):
|
||||
"""
|
||||
"""
|
||||
bl_label = "Modifier Profiling"
|
||||
bl_idname = "SCENE_PT_modifier_profiling"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "modifier"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
settings = scene.profiler_buddy_prop
|
||||
|
||||
layout.prop(settings, 'evaluation_live')
|
||||
|
||||
if settings.evaluation_live:
|
||||
draw_modifier_times_live(self, context)
|
||||
else:
|
||||
draw_modifier_times(self, context)
|
||||
col = layout.column()
|
||||
# col.prop(settings, 'use_frame_set')
|
||||
if settings.progress >= 0:
|
||||
col.progress(factor=settings.progress / settings.evaluations_count,
|
||||
text = f"Iteration {settings.progress}/{settings.evaluations_count}")
|
||||
## Show modifier sub-iteration with a load ring ?
|
||||
# col.progress(factor=settings.subprogress / len(context.object.modifiers),
|
||||
# type = 'RING') # , text = f"{settings.subprogress}/{len(context.object.modifiers)}"
|
||||
|
||||
row = col.row()
|
||||
row.prop(settings, 'evaluations_count')
|
||||
row.operator('pb.evaluate', text='Evaluate')
|
||||
|
||||
|
||||
|
||||
def time_to_string(t):
|
||||
''' Formats time in seconds to the nearest sensible unit.
|
||||
'''
|
||||
units = {3600.: 'h', 60.: 'm', 1.: 's', .001: 'ms'}
|
||||
for factor in units.keys():
|
||||
if t >= factor:
|
||||
return f'{t/factor:.3g} {units[factor]}'
|
||||
if t >= 1e-4:
|
||||
return f'{t/factor:.3g} {units[factor]}'
|
||||
else:
|
||||
return f'<0.1 ms'
|
||||
|
||||
## Dynamic draw
|
||||
|
||||
def draw_modifier_times_live(self, context):
|
||||
depsgraph = context.view_layer.depsgraph
|
||||
|
||||
ob = context.object
|
||||
ob_eval = ob.evaluated_get(depsgraph)
|
||||
|
||||
box = self.layout.box()
|
||||
times = []
|
||||
total = 0
|
||||
for mod_eval in ob_eval.modifiers:
|
||||
t = mod_eval.execution_time
|
||||
times += [t]
|
||||
total += t
|
||||
|
||||
col_fl = box.column_flow(columns=2)
|
||||
col = col_fl.column(align=True)
|
||||
for mod_eval in ob_eval.modifiers:
|
||||
row = col.row()
|
||||
row.enabled = mod_eval.show_viewport
|
||||
row.label(text=f'{mod_eval.name}:')
|
||||
|
||||
col = col_fl.column(align=True)
|
||||
for i, t in enumerate(times):
|
||||
row = col.row()
|
||||
row.enabled = ob_eval.modifiers[i].show_viewport
|
||||
row.alert = t >= 0.8*max(times)
|
||||
row.label(text=time_to_string(t))
|
||||
|
||||
row = box.column_flow()
|
||||
row.column().label(text=f'TOTAL:')
|
||||
row.column().label(text=time_to_string(sum(times)))
|
||||
|
||||
## Static draw from dict
|
||||
|
||||
def draw_modifier_times(self, context):
|
||||
box = self.layout.box()
|
||||
|
||||
ob = context.object
|
||||
wm = bpy.context.window_manager
|
||||
mod_timer = wm.get('mod_timer')
|
||||
if not mod_timer:
|
||||
mod_timer = wm['mod_timer'] = {}
|
||||
|
||||
if len(ob.modifiers) == 0:
|
||||
return
|
||||
|
||||
if mod_timer:
|
||||
# box.label(text=f'Average out of {context.scene.profiler_buddy_prop.evaluations_count}')
|
||||
times = [t for _m, t in mod_timer.items()]
|
||||
col_fl = box.column_flow(columns=2)
|
||||
col = col_fl.column(align=True)
|
||||
for i, (mod_name, t) in enumerate(mod_timer.items()):
|
||||
if i > len(ob.modifiers) - 1:
|
||||
continue
|
||||
row = col.row()
|
||||
row.enabled = ob.modifiers[i].show_viewport
|
||||
row.label(text=f'{mod_name}:')
|
||||
|
||||
col = col_fl.column(align=True)
|
||||
for i, (mod_name, t) in enumerate(mod_timer.items()):
|
||||
if i > len(ob.modifiers) - 1:
|
||||
continue
|
||||
row = col.row()
|
||||
row.enabled = ob.modifiers[i].show_viewport
|
||||
row.alert = t >= 0.8*max(times)
|
||||
row.label(text=time_to_string(t))
|
||||
|
||||
# col = col_fl.column(align=True)
|
||||
# for i, (mod_name, t) in enumerate(mod_timer.items()):
|
||||
# if i > len(ob.modifiers) - 1:
|
||||
# continue
|
||||
# row = col.row()
|
||||
# row.enabled = ob.modifiers[i].show_viewport
|
||||
# row.alert = t >= 0.8*max(times)
|
||||
# row.label(text=f'{1000 / (t*1000):.0f} fps')
|
||||
|
||||
row = box.column_flow()
|
||||
|
||||
total_time = sum(times)
|
||||
fps = f'{1000 / (total_time*1000):.1f} fps' if total_time > 0 else '-- fps'
|
||||
# row.label(text = fps)
|
||||
row.column().label(text=f'TOTAL: {fps}')
|
||||
|
||||
row.column().label(text=time_to_string(total_time))
|
||||
|
||||
|
||||
else:
|
||||
if context.scene.profiler_buddy_prop.progress < 0:
|
||||
box.label(text='Click Evaluate To Profile Modifiers', icon='INFO')
|
||||
|
||||
|
||||
class PB_OT_evalulate(Operator):
|
||||
bl_idname = "pb.evaluate"
|
||||
bl_label = "Evaluate Modifiers"
|
||||
bl_description = "Evaluate modifier by a count "
|
||||
bl_options = {"REGISTER"} # , "INTERNAL"
|
||||
|
||||
def execute(self, context):
|
||||
wm = bpy.context.window_manager
|
||||
|
||||
settings = context.scene.profiler_buddy_prop
|
||||
mod_timer = wm.get('mod_timer')
|
||||
if not mod_timer:
|
||||
mod_timer = wm['mod_timer'] = {}
|
||||
else:
|
||||
mod_timer = {}
|
||||
|
||||
wm['mod_timer'].clear() # Clear dict
|
||||
|
||||
tmp_dic = {}
|
||||
ob = context.object
|
||||
|
||||
wm.progress_begin(0, settings.evaluations_count)
|
||||
for i in range(settings.evaluations_count):
|
||||
settings.progress = i
|
||||
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
|
||||
# context.area.tag_redraw()
|
||||
ob.update_tag()
|
||||
# if settings.use_frame_set:
|
||||
# context.scene.frame_current = context.scene.frame_current
|
||||
|
||||
depsgraph = context.view_layer.depsgraph
|
||||
depsgraph.update()
|
||||
ob_eval = ob.evaluated_get(depsgraph)
|
||||
|
||||
## gather a list of all evaluated sessions
|
||||
for mod_eval in ob_eval.modifiers:
|
||||
if i == 0:
|
||||
tmp_dic[mod_eval.name] = []
|
||||
|
||||
t = mod_eval.execution_time
|
||||
# print(i, mod_eval.name, t)
|
||||
# wm['mod_timer'][mod_eval.name] = t # keep last evaluated time
|
||||
tmp_dic[mod_eval.name] += [t]
|
||||
|
||||
wm.progress_update(i)
|
||||
|
||||
wm.progress_end()
|
||||
|
||||
settings.progress = -1
|
||||
|
||||
for k, v in tmp_dic.items():
|
||||
average = sum(v) / len(v)
|
||||
# print(k, v, average)
|
||||
## debugging
|
||||
# if settings.evaluations_count > 1:
|
||||
# if v[0] == v[1]:
|
||||
# print('same:')
|
||||
# for subv in v:
|
||||
# print(subv)
|
||||
|
||||
wm['mod_timer'][k] = average
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class PB_PG_modifiers_profiler(PropertyGroup):
|
||||
evaluation_live : bpy.props.BoolProperty(name='Live Mode', default=True)
|
||||
evaluations_count : bpy.props.IntProperty(name='Evaluations', default=5)
|
||||
progress : bpy.props.IntProperty(name='Progress', default=-1)
|
||||
# subprogress : bpy.props.IntProperty(name='ModifierProgress', default=-1)
|
||||
# use_frame_set : bpy.props.BoolProperty(name='Set Frame At Each Iteration', default=True)
|
||||
|
||||
classes = [
|
||||
PB_OT_evalulate,
|
||||
PB_Modifier_Profiling_Panel,
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(PB_PG_modifiers_profiler)
|
||||
bpy.types.Scene.profiler_buddy_prop = PointerProperty(type=PB_PG_modifiers_profiler)
|
||||
bpy.context.window_manager['mod_timer'] = {}
|
||||
for c in classes:
|
||||
bpy.utils.register_class(c)
|
||||
|
||||
def unregister():
|
||||
for c in reversed(classes):
|
||||
bpy.utils.unregister_class(c)
|
||||
bpy.utils.unregister_class(PB_PG_modifiers_profiler)
|
||||
del bpy.types.Scene.profiler_buddy_prop
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
Loading…
Reference in New Issue