From b09403ff7a74e8529d4a1c5ab7efd01ed4d1bdaf Mon Sep 17 00:00:00 2001 From: Simon Wendsche Date: Mon, 27 Jan 2020 16:00:37 +0100 Subject: [PATCH] optimize frame_change_pre handler --- handlers/frame_change_pre.py | 40 ++++++++++++------------------------ handlers/load_post.py | 2 +- nodes/textures/imagemap.py | 2 +- nodes/textures/openVDB.py | 4 ++-- nodes/textures/smoke.py | 2 -- nodes/textures/timeinfo.py | 2 +- utils/node.py | 20 ++++++++++++++++++ 7 files changed, 38 insertions(+), 34 deletions(-) diff --git a/handlers/frame_change_pre.py b/handlers/frame_change_pre.py index 4ba5d3d3..76d118cc 100644 --- a/handlers/frame_change_pre.py +++ b/handlers/frame_change_pre.py @@ -4,47 +4,33 @@ from ..utils import openVDB_sequence_resolve_all from ..utils import clamp -# Flag that saves us from having to iterate all node trees when no image sequences are used (the common case). -# Set in image node export method, reset when new .blend is loaded in load_post. -using_image_sequences = False - -# Flag that saves us from having to iterate all node trees when no image sequences are used (the common case). -# Set in openVDB node export method, reset when new .blend is loaded in load_post. -using_smoke_sequences = False +RELEVANT_NODES = {"LuxCoreNodeTexImagemap", "LuxCoreNodeTexOpenVDB", "LuxCoreNodeTexTimeInfo"} +# Flags that save us from having to iterate all node trees when no related nodes are used (the common case). +# Set in relevant node export methods, reset when new .blend is loaded in load_post. +have_to_check_node_trees = False +# Important: Since this function is executed on every frame, even milliseconds of processin time in here will +# bring down the frame rate of animations considerably. Always assume the worst case: A big scene with many +# materials and complex node trees, and optimize for it. @persistent def handler(scene): - global using_image_sequences - global using_smoke_sequences + global have_to_check_node_trees - if not (using_image_sequences or using_smoke_sequences) or scene.render.engine != "LUXCORE": + if not have_to_check_node_trees or scene.render.engine != "LUXCORE": return - found_image_sequence = False - found_smoke_sequence = False + found_relevant_node = False for mat in bpy.data.materials: if not mat.luxcore.node_tree: continue - if (utils_node.has_nodes(mat.luxcore.node_tree, "LuxCoreNodeTexOpenVDB", True) or - utils_node.has_nodes(mat.luxcore.node_tree, "LuxCoreNodeTexSmoke", True)): - found_smoke_sequence = True - for node in mat.luxcore.node_tree.nodes: - if node.bl_idname == "LuxCoreNodeMatOutput" and node.active: - # Force a viewport update - mat.luxcore.node_tree.links.new(node.inputs["Material"], node.inputs["Material"].links[0].from_socket) - break - - for node in utils_node.find_nodes(mat.luxcore.node_tree, "LuxCoreNodeTexImagemap", True) or \ - utils_node.find_nodes(mat.luxcore.node_tree, "LuxCoreNodeTexTimeInfo", True): - found_image_sequence = True + if utils_node.has_nodes_multi(mat.luxcore.node_tree, RELEVANT_NODES, True): + found_relevant_node = True # Force a viewport update mat.diffuse_color = mat.diffuse_color - break - using_image_sequences = found_image_sequence - using_smoke_sequences = found_smoke_sequence + have_to_check_node_trees = found_relevant_node # TODO we are not handling area lights with image sequence textures right now # because I can't think of a check with good performance in large scenes. diff --git a/handlers/load_post.py b/handlers/load_post.py index 93c03f3c..f1ff8494 100644 --- a/handlers/load_post.py +++ b/handlers/load_post.py @@ -58,5 +58,5 @@ def handler(_): # Run converters for backwards compatibility compatibility.run() - frame_change_pre.using_image_sequences = False + frame_change_pre.have_to_check_node_trees = False LuxCoreErrorLog.clear() diff --git a/nodes/textures/imagemap.py b/nodes/textures/imagemap.py index b1a440f7..af87ab9c 100644 --- a/nodes/textures/imagemap.py +++ b/nodes/textures/imagemap.py @@ -182,7 +182,7 @@ def sub_export(self, exporter, depsgraph, props, luxcore_name=None, output_socke return [0, 0, 0] if self.image.source == "SEQUENCE": - frame_change_pre.using_image_sequences = True + frame_change_pre.have_to_check_node_trees = True try: filepath = ImageExporter.export(self.image, self.image_user, exporter.scene) diff --git a/nodes/textures/openVDB.py b/nodes/textures/openVDB.py index 661feade..ee5d2690 100644 --- a/nodes/textures/openVDB.py +++ b/nodes/textures/openVDB.py @@ -296,13 +296,13 @@ def sub_export(self, exporter, depsgraph, props, luxcore_name=None, output_socke file_path = self.get_cachefile_name(domain_eval, utils.clamp(frame, frame_start, frame_end), 0) if frame_end > frame_start: - frame_change_pre.using_smoke_sequences = True + frame_change_pre.have_to_check_node_trees = True else: indexed_filepaths = utils.openVDB_sequence_resolve_all(self.file_path) if len(indexed_filepaths) > 1: index, file_path = indexed_filepaths[utils.clamp(frame, self.first_frame, self.last_frame)-1] if self.last_frame > self.first_frame: - frame_change_pre.using_smoke_sequences = True + frame_change_pre.have_to_check_node_trees = True #Get transformation of domain bounding box, local center is lower bounding box corner scale = domain_eval.dimensions diff --git a/nodes/textures/smoke.py b/nodes/textures/smoke.py index 8656bd05..bbd7b373 100644 --- a/nodes/textures/smoke.py +++ b/nodes/textures/smoke.py @@ -9,7 +9,6 @@ from ...utils import node as utils_node from ...ui import icons from ...utils.errorlog import LuxCoreErrorLog -from ...handlers import frame_change_pre class LuxCoreNodeTexSmoke(bpy.types.Node, LuxCoreNodeTexture): bl_label = "Smoke" @@ -76,7 +75,6 @@ def sub_export(self, exporter, depsgraph, props, luxcore_name=None, output_socke } return self.create_props(props, definitions, luxcore_name) - frame_change_pre.using_smoke_sequences = True domain_eval = self.domain.evaluated_get(depsgraph) scale = domain_eval.dimensions diff --git a/nodes/textures/timeinfo.py b/nodes/textures/timeinfo.py index 0e6f2521..769b7b90 100644 --- a/nodes/textures/timeinfo.py +++ b/nodes/textures/timeinfo.py @@ -28,7 +28,7 @@ def draw_buttons(self, context, layout): def sub_export(self, exporter, depsgraph, props, luxcore_name=None, output_socket=None): scene = depsgraph.scene_eval - frame_change_pre.using_image_sequences = True + frame_change_pre.have_to_check_node_trees = True definitions = { "type": "constfloat1", diff --git a/utils/node.py b/utils/node.py index e414c31e..4621d50d 100644 --- a/utils/node.py +++ b/utils/node.py @@ -163,6 +163,26 @@ def has_nodes(node_tree, bl_idname, follow_pointers): return False +def has_nodes_multi(node_tree, bl_idname_set, follow_pointers): + for node in node_tree.nodes: + if follow_pointers and node.bl_idname == "LuxCoreNodeTreePointer" and node.node_tree: + try: + if has_nodes_multi(node.node_tree, bl_idname_set, follow_pointers): + return True + except RecursionError: + msg = (f'Pointer nodes in node trees "{node_tree.name}" and "{node.node_tree.name}" ' + "create a dependency cycle! Delete one of them.") + LuxCoreErrorLog.add_error(msg) + # Mark the faulty nodes in red + node.use_custom_color = True + node.color = (0.9, 0, 0) + return False + if node.bl_idname in bl_idname_set: + return True + + return False + + def force_viewport_update(_, context): """ Since Blender 2.80, properties on custom sockets and custom nodes are not listed