From 05688be3becab61a2a78f4faed8ae7198e2d6180 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 21 Dec 2017 16:32:56 +0100 Subject: Depsgrapg debug: Add buttons to show evaluation statistics --- depsgraph_debug.py | 178 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 41 deletions(-) diff --git a/depsgraph_debug.py b/depsgraph_debug.py index 6e0b48f0..9e4c67f0 100644 --- a/depsgraph_debug.py +++ b/depsgraph_debug.py @@ -44,11 +44,10 @@ def _get_depsgraph(context): return scene_layer.depsgraph -class SCENE_OT_depsgraph_graphviz(Operator): - bl_idname = "scene.depsgraph_graphviz" - bl_label = "Save Depsgraph" - bl_description = "Save current scene's dependency graph to a graphviz file" +############################################################################### +# Save data from depsgraph to a specified file. +class SCENE_OT_depsgraph_save_common: filepath = StringProperty( name="File Path", description="Filepath used for saving the file", @@ -56,6 +55,9 @@ class SCENE_OT_depsgraph_graphviz(Operator): subtype='FILE_PATH', ) + def _getExtension(self, context): + return "" + @classmethod def poll(cls, context): depsgraph = _get_depsgraph(context) @@ -70,23 +72,53 @@ class SCENE_OT_depsgraph_graphviz(Operator): else: blend_filepath = os.path.splitext(blend_filepath)[0] - self.filepath = blend_filepath + ".dot" - + self.filepath = blend_filepath + self._getExtension(context) context.window_manager.fileselect_add(self) - return {'RUNNING_MODAL'} def execute(self, context): depsgraph = _get_depsgraph(context) - depsgraph.debug_graphviz(self.filepath) + if not self.performSave(context, depsgraph): + return {'CANCELLED'} return {'FINISHED'} + def performSave(self, context, depsgraph): + pass -class SCENE_OT_depsgraph_image(Operator): - bl_idname = "scene.depsgraph_image" - bl_label = "Depsgraph as Image" - bl_description = "Create new image datablock from the dependency graph" +class SCENE_OT_depsgraph_relations_graphviz(Operator, + SCENE_OT_depsgraph_save_common): + bl_idname = "scene.depsgraph_relations_graphviz" + bl_label = "Save Depsgraph" + bl_description = "Save current scene's dependency graph to a graphviz file" + + def _getExtension(self, context): + return ".dot" + + def performSave(self, context, depsgraph): + basename, extension = os.path.splitext(self.filepath) + depsgraph.debug_relations_graphviz(self.filepath, absename + ".png") + return True + + +class SCENE_OT_depsgraph_stats_gnuplot(Operator, + SCENE_OT_depsgraph_save_common): + bl_idname = "scene.depsgraph_stats_gnuplot" + bl_label = "Save Depsgraph Stats" + bl_description = "Save current scene's evaluaiton stats to gnuplot file" + + def _getExtension(self, context): + return ".plot" + + def performSave(self, context, depsgraph): + depsgraph.debug_stats_gnuplot(self.filepath, "") + return True + + +############################################################################### +# Visualize some depsgraph information as an image opening in image editor. + +class SCENE_OT_depsgraph_image_common: def _getOrCreateImageForAbsPath(self, filepath): for image in bpy.data.images: if image.filepath == filepath: @@ -109,37 +141,107 @@ class SCENE_OT_depsgraph_image(Operator): return space return first_none_editor + def _createTempFile(self, suffix): + import os + import tempfile + fd, filepath = tempfile.mkstemp(suffix=suffix) + os.close(fd) + return filepath + + def _openImageInEditor(self, context, image_filepath): + image = self._getOrCreateImageForAbsPath(image_filepath) + editor = self._findBestImageEditor(context, image) + if editor: + editor.image = image + def execute(self, context): + depsgraph = _get_depsgraph(context) + if not self.performSave(context, depsgraph): + return {'CANCELLED'} + return {'FINISHED'} + + def performSave(self, context, depsgraph): + pass + + +class SCENE_OT_depsgraph_relations_image(Operator, + SCENE_OT_depsgraph_image_common): + bl_idname = "scene.depsgraph_relations_image" + bl_label = "Depsgraph as Image" + bl_description = "Create new image datablock from the dependency graph" + + def performSave(self, context, depsgraph): import os import subprocess - import tempfile # Create temporary file. - fd, dot_filepath = tempfile.mkstemp(suffix=".dot") - os.close(fd) + dot_filepath = self._createTempFile(suffix=".dot") # Save dependency graph to graphviz file. - depsgraph = _get_depsgraph(context) - depsgraph.debug_graphviz(dot_filepath) + depsgraph.debug_relations_graphviz(dot_filepath) # Convert graphviz to PNG image. png_filepath = os.path.join(bpy.app.tempdir, "depsgraph.png") command = ("dot", "-Tpng", dot_filepath, "-o", png_filepath) - image = None try: subprocess.run(command) - # Open image in Blender. - image = self._getOrCreateImageForAbsPath(png_filepath) + self._openImageInEditor(context, png_filepath) except: self.report({'ERROR'}, "Error invoking dot command") - return {'CANCELLED'} + return False finally: # Remove graphviz file. os.remove(dot_filepath) - editor = self._findBestImageEditor(context, image) - if editor: - editor.image = image - return {'FINISHED'} + return True + + +class SCENE_OT_depsgraph_stats_image(Operator, + SCENE_OT_depsgraph_image_common): + bl_idname = "scene.depsgraph_stats_image" + bl_label = "Depsgraph Stats as Image" + bl_description = "Create new image datablock from the dependency graph " + \ + "execution statistics" + + def performSave(self, context, depsgraph): + import os + import subprocess + # Create temporary file. + plot_filepath = self._createTempFile(suffix=".plot") + png_filepath = os.path.join(bpy.app.tempdir, "depsgraph_stats.png") + # Save dependency graph stats to gnuplot file. + depsgraph.debug_stats_gnuplot(plot_filepath, png_filepath) + # Convert graphviz to PNG image. + command = ("gnuplot", plot_filepath) + try: + subprocess.run(command) + self._openImageInEditor(context, png_filepath) + except: + self.report({'ERROR'}, "Error invoking gnuplot command") + return False + finally: + # Remove graphviz file. + os.remove(plot_filepath) + return True + + +############################################################################### +# Interface. -class SCENE_PT_depsgraph(bpy.types.Panel): +class SCENE_PT_depsgraph_common: + def draw(self, context): + layout = self.layout + col = layout.column() + # Everything related on relations and graph topology. + col.label(text="Relations:") + row = col.row() + row.operator("scene.depsgraph_relations_graphviz") + row.operator("scene.depsgraph_relations_image") + # Everything related on evaluaiton statistics. + col.label(text="Statistics:") + row = col.row() + row.operator("scene.depsgraph_stats_gnuplot") + row.operator("scene.depsgraph_stats_image") + + +class SCENE_PT_depsgraph(bpy.types.Panel, SCENE_PT_depsgraph_common): bl_label = "Dependency Graph" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" @@ -153,13 +255,8 @@ class SCENE_PT_depsgraph(bpy.types.Panel): depsgraph = _get_depsgraph(context) return depsgraph is not None - def draw(self, context): - layout = self.layout - layout.operator("scene.depsgraph_graphviz") - layout.operator("scene.depsgraph_image") - -class RENDERLAYER_PT_depsgraph(bpy.types.Panel): +class RENDERLAYER_PT_depsgraph(bpy.types.Panel, SCENE_PT_depsgraph_common): bl_label = "Dependency Graph" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" @@ -173,22 +270,21 @@ class RENDERLAYER_PT_depsgraph(bpy.types.Panel): depsgraph = _get_depsgraph(context) return depsgraph is not None - def draw(self, context): - layout = self.layout - layout.operator("scene.depsgraph_graphviz") - layout.operator("scene.depsgraph_image") - def register(): - bpy.utils.register_class(SCENE_OT_depsgraph_graphviz) - bpy.utils.register_class(SCENE_OT_depsgraph_image) + bpy.utils.register_class(SCENE_OT_depsgraph_relations_graphviz) + bpy.utils.register_class(SCENE_OT_depsgraph_relations_image) + bpy.utils.register_class(SCENE_OT_depsgraph_stats_gnuplot) + bpy.utils.register_class(SCENE_OT_depsgraph_stats_image) bpy.utils.register_class(SCENE_PT_depsgraph) bpy.utils.register_class(RENDERLAYER_PT_depsgraph) def unregister(): - bpy.utils.unregister_class(SCENE_OT_depsgraph_graphviz) - bpy.utils.unregister_class(SCENE_OT_depsgraph_image) + bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_graphviz) + bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_image) + bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_gnuplot) + bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_image) bpy.utils.unregister_class(SCENE_PT_depsgraph) bpy.utils.unregister_class(RENDERLAYER_PT_depsgraph) -- cgit v1.2.3