nortikin / sverchok

Sverchok
http://nortikin.github.io/sverchok/
GNU General Public License v3.0
2.25k stars 233 forks source link

Heat map of node times #235

Closed zeffii closed 10 years ago

zeffii commented 10 years ago

First I thought i'll redraw the tree using browser+d3.js by parsing the node tree in blender etc, that may still happen to allow for a SVG of the node tree layout. I asked Sergey of BF if there was any thoughts on outputting the openGL px buffer of the node tree as a png, it's possible but not going on the roadmap -- this was no surpise, thats okay.

But then i thought it's easiest, and might even make more sense, to output the heat-map on the nodeview as ui colors. White -> Red (white for fast, red for heavy) (or whatever colours make you happy)

Something like this ( this doesn't really work yet (just mentally compile), but it's close - I haven't had time to finish it. )

import bpy

ng = bpy.data.node_groups[0]

restore_colors = {}
for node in ng.nodes:
    node_info = {
        'custom': node.use_custom_color,
        'color': node.color,
    }

    color_view = node.getattr('color_view', None)
    if color_view:
        node_info['color_view'] = node.color_view

    restore_colors[node.name] = node_info

for name, info in restore_colors.items():
    # ng.nodes[name].use_custom_color = True
    ng.nodes[name].color = (0.5, 0.8, 0.8) # remap to update time

Then restore the nodes

# restore
for node, info in restore_colors.items():
    ng.nodes[name].use_custom_color = info['custom']
    ng.nodes[name].color = info['color']
    if 'color_view' in info:
        ng.nodes[name].color_view = info['color_view']
ly29 commented 10 years ago

This works, better to just set both values back. Also a good idea to use node.name instead of node due to undo etc, of course renaming nodes while playing with this will stop them from being restored. Also if you save/quit while doing this will loose your data.

#collect color data
color_data = {node.name:(node.color.copy(),node.use_custom_color)for node in ng.nodes}

# restore color
for name,node in ng.nodes.items():
    if name in color_data:
        color,use = color_data[name]
        setattr(node,'color',color)
        setattr(node,'use_custom_color',use)
ly29 commented 10 years ago

Some simple examples how it could look. Doing this properly is dependent on #187 but for now we could make an debug panel and expose different timings options. this works per node groups but doesn't restore right now. Also linear scale from max to 0 isn't that useful

heat-map Simple example heat-map-spider2 spider by @nortikin spider-2 spider by @enzyme69 (earlier picture of broke setup)

An alternative do_update function

def do_update_heat_map(node_list, nodes):
    times = []
    # node_list is a iterator sometimes
    node_list = list(node_list)
    for name in node_list:
        if name in nodes:
            start = time.perf_counter()
            nodes[name].update()
            delta = time.perf_counter()-start
            print("Updated  {0} in:{1}".format(name, round(delta, 4)))
            times.append(delta)
    if not times:
        return
    t_max = max(times)
    red=Vector((1,0,0))
    white=Vector((1,1,1))
    for name,t in zip(node_list, times):
        nodes[name].use_custom_color =  True
        # linerar scale.
        nodes[name].color = white.lerp( red,t/t_max)
ly29 commented 10 years ago

smaller-scale

zeffii commented 10 years ago

damnit, I was looking forward to coding this -- but there you go! Does it make sense to do a log scale?

Do you find the heatmap to be practical @ly29? I think it will help people figure out a few things. Clearly there are options to explore here, not high priority but interesting visualization questions.

ly29 commented 10 years ago

I had something similar laying around ( colored by order of updates), but this isn't close to finished by having two snippets and demo coloring code. The snippets saving/restoring color state must plugged in somewhere where they make sense, the colors have to be stored in smart way, the code has to become layout aware. It is a simple and useful way to see what operations are expensive.

zeffii commented 10 years ago

I think it looks really cool, beside it being useful. Maybe (time / total_summed_time) to indicate relative expense

zeffii commented 10 years ago

node tree profiling.

ly29 commented 10 years ago

I tried that at first but it looked too flat when you have many many nodes.

nortikin commented 10 years ago

so at the end we should got three color schemes:

  1. Usual - colors for text nodes blue, for object in node green, for output orange, for wifi viol, etc.
  2. Timing - colored in update time.
  3. Update - colored in update order. First scheme for usual usage, default, second and last - for debugging.
ly29 commented 10 years ago

@zeffii To test just plug that into util or data structure and change in.

    if DEBUG_MODE:
        do_update = do_update_heat_map

@nortikin I think two is enough. The last one isn't that useful except for when writing the update function.

zeffii commented 10 years ago

I think to prevent risky user interaction, this could be some kind of operator which doesn't let user interact with the node tree (beyond zoom, pan) while heat-map colors are shown. Exactly how to implement that isn't clear to me. Will try do_update_heat_map later

ly29 commented 10 years ago

It should be solved before committing it. Also we should do some performance tests on the draw callbacks.

ly29 commented 10 years ago

I guess we could just serialize the user colors to the node tree as string property, so we don't loose them, that seems like the easiest option to make it persistent.

zeffii commented 10 years ago

what's the size limit of a StringProperty? :D

zeffii commented 10 years ago

length_max: Maximum length of the string, 0 means unlimited uh oh. I might abuse this

ly29 commented 10 years ago

Bigger than we need, we I have stored the verts of 1000x1000 plane in a StringProperty.

ly29 commented 10 years ago

it is best avoid doing things this way, but for this purpose I think it fits.

zeffii commented 10 years ago

clearly serial-deserial-ize is not an operation you want to do for 1m verts as strings on each update.

ly29 commented 10 years ago

If we have a stringproperty called sv_user_colors

def heat_map_state(state):
    global HEAT_MAP
    HEAT_MAP = state
    sv_ng = [ng for ng in bpy.data.node_groups if ng.bl_idname == 'SverchCustomTreeType']
    if state:
        for ng in sv_ng:
            color_data = {node.name: (node.color[:], node.use_custom_color) for node in ng.nodes}
            ng.sv_user_colors = str(color_data)
    else:
        for ng in sv_ng:
            color_data = eval(ng.sv_user_colors)
            for name, node in ng.nodes.items():
                if name in color_data:
                    color, use = color_data[name]
                    setattr(node, 'color', color)
                    setattr(node, 'use_custom_color', use)
            ng.sv_user_colors = ""
zeffii commented 10 years ago

cool, multi layout

ly29 commented 10 years ago

Somewhat related, was also thinking about putting a condensed timings report in a text buffer

zeffii commented 10 years ago

stats? mean, total, outliers - could easily draw to nodeview with greasepencil text as an operator

zeffii commented 10 years ago

or bgl+blf directly into node_view

zeffii commented 10 years ago

maybe also off-topic: I like the way grasshopper displays information in the node editor, I had imagined (before seeing the screenshot of grasshopper) that displaying head+tail of list content directly in the tree or beside the socket on hover over would be an excellent way to see data quickly-summarized instead of TextEditor/Console

ly29 commented 10 years ago

yes all of those things would help so much with the interactive experience. also been thinking of mode or something take takes vertices from sockets of active node and visualizes (if possible) and other such things. heat-map-pref

It kind of works now, of course if one triggers a partial update only the affected nodes are updated which makes the color scale somewhat confusing etc.

zeffii commented 10 years ago

visualize vertices in node_view? yeah, like compositor backdrop. Would also allow for custom mouse / kb view rotations

ly29 commented 10 years ago

demo-map kind of works in the way that I am not happy how the colors are distributed.

I wasn't thinking as a backdrop but to draw them say in red in the 3d view.

zeffii commented 10 years ago

box node is clunky due to repeated bmesh.ops I bet the original (broken?) code is faster.

Color distribution, you need a gamma curve?

ly29 commented 10 years ago

How the colors are distributed from the settings to the function. Anyway this needs solved in a general way... Also the color scale isn't that good but that we can always tune a bit, that is for fine tuning.

nortikin commented 10 years ago

yes, first node too red.

ly29 commented 10 years ago

Yes, both for what it does and for the pure color itself. Right now is all relative so the slowest node gets the hot color.

ly29 commented 10 years ago

another-example It looks like works okay now, stores and restore user colors, in save safe way. Also preferences are remembered now. Suggestions for hot/cold default values? Code for mapping between them in a simple way would also be good.

ly29 commented 10 years ago

Current code for coloring creates a vector from on of the colors to user simple linear interpolation `lerp``. Note that this per separate noodle right now.

    t_max = max(times)
    for name, t in zip(node_list, times):
        nodes[name].use_custom_color = True
        nodes[name].color = cold.lerp(hot, t / t_max)

I tried with t_sum = sum(times) but for bigger number of nodes they all become similar. Things I am not happy about, addon name is hardcoded.

addon = bpy.context.user_preferences.addons.get("sverchok")
ly29 commented 10 years ago

Okay, pushed, now, go and play! 05d5006

ly29 commented 10 years ago

Changing defaults and testing different ways to color is actively left to you.

zeffii commented 10 years ago

works nicely, I'll probably stick to the default colors; it's immediately clear where the cycles are accumulating with any sane colour combo. Very nice indeed.

ly29 commented 10 years ago

I think using bgl is a better option for this honestly. Like frame around each node. with the color...

zeffii commented 10 years ago

what are you thinking @ly29 , draw a table of top offenders (with option to exclude certain nodes) and draw lines to each node from table index? (maybe offer to zoom to the area of each offender)

ly29 commented 10 years ago

I was thinking to just draw a frame around the nodes in the appropriate colors. It is worth exploring, it feels slightly wrong the whole acrobatics with storing the user colors if we can just draw in open gl. My original thought was grease pencil but creating a huge number of layers (well 10 or so) for the different colors didn't feel that good.

zeffii commented 10 years ago

The main reason I agree is because when nodes are minimized that border is all that gets coloured. This outline is too thin imo to be eyecatching. But I do like the visual feedback of the nodes themselves getting the heat colour. Is your issue with this method the way it is achieved and the potential that something could interrupt the process leaving us with permanent colourmapped nodes for that layout?

I think POST_PIXEL drawing a table with bgl and blf to the left or right of the view, with PRE_PIXEL direct lines from table index to the nodes and outlines so they move with the node tree panning. http://bost.ocks.org/mike/sankey/ http://bost.ocks.org/mike/uberdata/

ly29 commented 10 years ago

It seems like abusing a useful an feature for organizing layouts an for giving temporary info. All nodes added while it is active keep getting the color assigned while color etc, all this can be solved but feels like additional layers to keep track of everywhere. It feels dirty.

zeffii commented 10 years ago

I agree, the process is dirty, the result isn't.

zeffii commented 10 years ago

not really moving is it.. close for now