Custom nodes not showing as updated in interactive mode

Having a slight problem implementing interactive rendering for appleseed in Blender 2.8. I’m attempting to utilize the new depsgraph.updates list. While this works great for object changes, there are some missing items. For example, we use a custom node tree for our shaders. Changing a node parameter triggers a RenderEngine.view_update call, but the updated node tree does not appear in the depsgraph.updates list. While I can find out that a node tree did change using the depsgraph.id_type_updated function, to find the actual tree that changed I will have to compare every single node tree in my scene (which could easily be several dozen or more). This could easily get to be a bottleneck.

Am I missing something or is this a bug?

Edit: I see that the build in nodetree for a material shows up in the depsgraph.updates list properly. I’ve debated switching over to this node tree but I haven’t found a way to hide the Cycles nodes from showing up even when appleseed is the selected renderer.

Sure that you have declared a correct context on your ‘nodetree’ definition?

    class TheBountyMaterialNodeTree(bpy.types.ShaderNodeTree):
    bl_idname = 'TheBountyMaterialNodeTree'
    bl_label = 'TheBounty NodeTree'
    bl_icon = 'MATERIAL'
    MatNodeTypes = {}

    def poll(cls, context):
        return context.scene.render.engine == 'THEBOUNTY'

    def get_from_context(cls, context):
        ob = context.active_object
        if ob and ob.type not in {'LIGHT', 'CAMERA'}:
            mat = ob.active_material
            if mat != None:
                nt_name = mat.bounty.nodetree
                if nt_name != '':
                    return[nt_name], ob, mat

        return (None, None, None)

Yes, the get_from_context works properly and displays the correct nodetree.

class AppleseedOSLNodeTree(bpy.types.ShaderNodeTree):
    """Class for appleseed node tree."""

    bl_idname = 'AppleseedOSLNodeTree'
    bl_label = 'appleseed OSL Node Tree'
    bl_icon = 'MATERIAL'
    MatNodeTypes = {}

    def get_from_context(cls, context):
        Switches the displayed node tree when user selects object/material
        obj = context.active_object

        if obj and obj.type not in {"LIGHT", "CAMERA"}:
            mat = obj.active_material

            if mat:
                # ID pointer
                node_tree = mat.appleseed.osl_node_tree

                if node_tree:
                    return node_tree, obj, mat

        elif obj and obj.type == "LIGHT":
            node_tree =

            if node_tree:
                return node_tree, obj, None

        return None, None, None

    def poll(cls, context):
        renderer = context.scene.render.engine
        return renderer == 'APPLESEED_RENDER'

    def update(self):
        if hasattr(bpy.context, "material"):
            bpy.context.material.preview_render_type = bpy.context.material.preview_render_type

That’s not the issue I’m describing.

I’m referring to when the node tree is updated during interactive render mode. In 2.79b we used to detect that a tree had changed by using the ‘is_updated’ flag on that tree. That flag doesn’t exist in 2.8. Instead it appears we’re supposed to use ‘context.depsgraph.updates’ to find out what changed. Updated custom node trees do not appear in this list, ever.

The scene update is triggered when a node tree is changed, so something somewhere is tracking that properly, but it doesn’t make sense that the node tree doesn’t appear in the depsgraph.updates list.

My answer was related to the problem you describe here. That’s why I marked it in my response.

Oh. I guess I wasn’t clear what I was referring to:

When I try putting appleseed nodes into the built in nodetree that comes with each material ([‘Material’].node_tree), the Cycles nodes still appear even when I have appleseed as the active renderer. I would rather not mix our nodes with Cycles, but right now it’s the only reasonable way to update materials and node trees without re-exporting everything.

That is because the poll function for most of Cycles’ nodes comes from the ShaderNodeCategory, which just checks if the node_tree is a ShaderNodeTree.
We need to fix this in startup\

I have tried a temporary solution, which avoids this ‘mix’ deriving my class from Nodetree class instead of ShaderNodeTree. But maybe is only a ‘patch’…

That’s what we were doing in 2.79b, but as mentioned any custom trees derived from either bpy.types.NodeTree or bpy.types.ShaderNodeTree do not get added to the depsgraph.updates list when they update in 2.8, so there’s no way of detecting exactly what node tree changed.

@Secrop is that something that can be fixed for 2.8 (the startup/ update)? Should I file a bug report?

It’s possible to rewrite both nodeitems_utils and nodeitems_builtins to make them more practical for custom nodes/nodetrees… I’ve already have some code writen for custom nodes that could be used here, and I could try to expand those ideas to custom nodetrees. It might require that custom nodetrees need to be rewriten as well (at least the registration part).
I’ll try to sketch a proposal and post it back here for discussion. It would be nice to ear the opinion from other nodetree developers.

That would be great!

The automatic node updates is working properly here… ( sorry, some nodes are WIP)
Material nodes

1 Like

You’re showing the preview window. That updates properly for me as well. Again, I am referring to interactive viewport rendering.

Test it yourself, add this line to the view_update function in the RenderEngine:

def view_update(self, context):
    for obj in context.depsgraph.updates:

Now enter rendered viewport mode, and try updating something in your node tree. The view_update function will trigger, but the only thing that will appear in the updates list is the Scene datablock.

Now, on the other hand, if you update the node tree that contains the Cycles nodes (the built in material nodetree), the material block itself will show up in that list, giving you a nice, easy way to update your render scene.

To hide Cycles nodes when custom render engine is active(selected) you can use this approach:

old_shader_node_category_poll = None # keep reference to original poll method

def hide_cycles_and_eevee_poll(method):
def func(cls, context): # wrapping Cycles poll method to check if your addon is not active at the moment
return not context.scene.render.engine == ‘RENDER_ENGINE_ID’ and method(context)
return func

def register():
global old_shader_node_category_poll
old_shader_node_category_poll = ShaderNodeCategory.poll
ShaderNodeCategory.poll = hide_cycles_and_eevee_poll(ShaderNodeCategory.poll)
(… register your nodes/classes …)

def unregister():
if old_shader_node_category_poll and ShaderNodeCategory.poll is not old_shader_node_category_poll:
ShaderNodeCategory.poll = old_shader_node_category_poll
(… unregister your nodes/classes here …)

It could be the overkill but it works.
The idea is to wrap poll method using wrapper when addon is activated and restore original when on unloading stage. This way you’ll be able to utilize existing Cycles node tree using only custom nodes or Cycles nodes you have specifically added to the menu. For it you’ll need to use some CustomShaderNodeCategory(NodeCategory) class with poll method to enable it in your addon only.

This might create conflicts with other addons… It’s better to change the nodeitems_builtins and add a specific poll to all builtin shader nodes.

Hmm, that’s one more potential issue to check. Thank you.

Now enter rendered viewport mode, and try updating something in your node tree. The view_update function will trigger, but the only thing that will appear in the updates list is the Scene datablock.

Sorry… I don’t have IPR support yet on my renderer :frowning: