Profiling buddy
parent
3407bc79e9
commit
78a7d6f900
10
README.md
10
README.md
|
@ -1,7 +1,9 @@
|
||||||
# GIT_TEMPLATE
|
# PROFILING BUDDY
|
||||||
> Template for the others repository
|
|
||||||
|
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 -->
|
<!-- 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
|
2. Create your own local directory in
|
||||||
```sh
|
```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 -->
|
<!-- 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