diff options
Diffstat (limited to 'io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py')
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py | 184 |
1 files changed, 122 insertions, 62 deletions
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py index 7e49ac02..4f95431c 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_cache.py @@ -6,83 +6,134 @@ import bpy from io_scene_gltf2.blender.exp import gltf2_blender_get -def cached(func): +def cached_by_key(key): + """ + Decorates functions whose result should be cached. Use it like: + @cached_by_key(key=...) + def func(..., export_settings): + ... + The decorated function, func, must always take an "export_settings" arg + (the cache is stored here). + The key argument to the decorator is a function that computes the key to + cache on. It is passed all the arguments to func. """ - Decorate the cache gather functions results. + def inner(func): + @functools.wraps(func) + def wrapper_cached(*args, **kwargs): + if kwargs.get("export_settings"): + export_settings = kwargs["export_settings"] + else: + export_settings = args[-1] + + cache_key = key(*args, **kwargs) + + # invalidate cache if export settings have changed + if not hasattr(func, "__export_settings") or export_settings != func.__export_settings: + func.__cache = {} + func.__export_settings = export_settings + # use or fill cache + if cache_key in func.__cache: + return func.__cache[cache_key] + else: + result = func(*args, **kwargs) + func.__cache[cache_key] = result + return result + + return wrapper_cached + + return inner - The gather function is only executed if its result isn't in the cache yet - :param func: the function to be decorated. It will have a static __cache member afterwards - :return: + +def default_key(*args, **kwargs): + """ + Default cache key for @cached functions. + Cache on all arguments (except export_settings). """ + assert len(args) >= 2 and 0 <= len(kwargs) <= 1, "Wrong signature for cached function" + cache_key_args = args + # make a shallow copy of the keyword arguments so that 'export_settings' can be removed + cache_key_kwargs = dict(kwargs) + if kwargs.get("export_settings"): + del cache_key_kwargs["export_settings"] + else: + cache_key_args = args[:-1] + + cache_key = () + for i in cache_key_args: + cache_key += (i,) + for i in cache_key_kwargs.values(): + cache_key += (i,) + + return cache_key + + +def cached(func): + return cached_by_key(key=default_key)(func) + +def objectcache(func): + + def reset_cache_objectcache(): + func.__objectcache = {} + + func.reset_cache = reset_cache_objectcache + @functools.wraps(func) - def wrapper_cached(*args, **kwargs): - assert len(args) >= 2 and 0 <= len(kwargs) <= 1, "Wrong signature for cached function" + def wrapper_objectcache(*args, **kwargs): cache_key_args = args - # make a shallow copy of the keyword arguments so that 'export_settings' can be removed - cache_key_kwargs = dict(kwargs) - if kwargs.get("export_settings"): - export_settings = kwargs["export_settings"] - # 'export_settings' should not be cached - del cache_key_kwargs["export_settings"] - else: - export_settings = args[-1] - cache_key_args = args[:-1] + cache_key_args = args[:-1] - __by_name = [bpy.types.Object, bpy.types.Scene, bpy.types.Material, bpy.types.Action, bpy.types.Mesh, bpy.types.PoseBone] + if not hasattr(func, "__objectcache"): + func.reset_cache() - # we make a tuple from the function arguments so that they can be used as a key to the cache - cache_key = () - for i in cache_key_args: - if type(i) in __by_name: - cache_key += (i.name,) - else: - cache_key += (i,) - for i in cache_key_kwargs.values(): - if type(i) in __by_name: - cache_key += (i.name,) - else: - cache_key += (i,) - - # invalidate cache if export settings have changed - if not hasattr(func, "__export_settings") or export_settings != func.__export_settings: - func.__cache = {} - func.__export_settings = export_settings - # use or fill cache - if cache_key in func.__cache: - return func.__cache[cache_key] - else: + # object is not cached yet + if cache_key_args[0] not in func.__objectcache.keys(): result = func(*args) - func.__cache[cache_key] = result - return result - return wrapper_cached + func.__objectcache = result + return result[cache_key_args[0]][cache_key_args[1]][cache_key_args[4]] + # object is in cache, but not this action + # We need to keep other actions + elif cache_key_args[1] not in func.__objectcache[cache_key_args[0]].keys(): + result = func(*args) + func.__objectcache[cache_key_args[0]][cache_key_args[1]] = result[cache_key_args[0]][cache_key_args[1]] + return result[cache_key_args[0]][cache_key_args[1]][cache_key_args[4]] + # all is already cached + else: + return func.__objectcache[cache_key_args[0]][cache_key_args[1]][cache_key_args[4]] + return wrapper_objectcache def bonecache(func): def reset_cache_bonecache(): func.__current_action_name = None - func.__current_armature_name = None + func.__current_armature_uuid = None func.__bonecache = {} func.reset_cache = reset_cache_bonecache @functools.wraps(func) def wrapper_bonecache(*args, **kwargs): - if args[2] is None: - pose_bone_if_armature = gltf2_blender_get.get_object_from_datapath(args[0], - args[1][0].data_path) + + armature = args[-1]['vtree'].nodes[args[0]].blender_object + + cache_key_args = args + cache_key_args = args[:-1] + + if cache_key_args[2] is None: + pose_bone_if_armature = gltf2_blender_get.get_object_from_datapath(armature, + cache_key_args[1][0].data_path) else: - pose_bone_if_armature = args[0].pose.bones[args[2]] + pose_bone_if_armature = armature.pose.bones[cache_key_args[2]] if not hasattr(func, "__current_action_name"): func.reset_cache() - if args[6] != func.__current_action_name or args[0] != func.__current_armature_name: + if cache_key_args[6] != func.__current_action_name or cache_key_args[0] != func.__current_armature_uuid: result = func(*args) func.__bonecache = result - func.__current_action_name = args[6] - func.__current_armature_name = args[0] - return result[args[7]][pose_bone_if_armature.name] + func.__current_action_name = cache_key_args[6] + func.__current_armature_uuid = cache_key_args[0] + return result[cache_key_args[7]][pose_bone_if_armature.name] else: - return func.__bonecache[args[7]][pose_bone_if_armature.name] + return func.__bonecache[cache_key_args[7]][pose_bone_if_armature.name] return wrapper_bonecache # TODO: replace "cached" with "unique" in all cases where the caching is functional and not only for performance reasons @@ -92,23 +143,27 @@ unique = cached def skdriverdiscovercache(func): def reset_cache_skdriverdiscovercache(): - func.__current_armature_name = None + func.__current_armature_uuid = None func.__skdriverdiscover = {} func.reset_cache = reset_cache_skdriverdiscovercache @functools.wraps(func) def wrapper_skdriverdiscover(*args, **kwargs): - if not hasattr(func, "__current_armature_name") or func.__current_armature_name is None: + + cache_key_args = args + cache_key_args = args[:-1] + + if not hasattr(func, "__current_armature_uuid") or func.__current_armature_uuid is None: func.reset_cache() - if args[0] != func.__current_armature_name: + if cache_key_args[0] != func.__current_armature_uuid: result = func(*args) - func.__skdriverdiscover[args[0]] = result - func.__current_armature_name = args[0] + func.__skdriverdiscover[cache_key_args[0]] = result + func.__current_armature_uuid = cache_key_args[0] return result else: - return func.__skdriverdiscover[args[0]] + return func.__skdriverdiscover[cache_key_args[0]] return wrapper_skdriverdiscover def skdrivervalues(func): @@ -123,12 +178,17 @@ def skdrivervalues(func): if not hasattr(func, "__skdrivervalues") or func.__skdrivervalues is None: func.reset_cache() - if args[0].name not in func.__skdrivervalues.keys(): - func.__skdrivervalues[args[0].name] = {} - if args[1] not in func.__skdrivervalues[args[0].name]: + armature = args[-1]['vtree'].nodes[args[0]].blender_object + + cache_key_args = args + cache_key_args = args[:-1] + + if armature.name not in func.__skdrivervalues.keys(): + func.__skdrivervalues[armature.name] = {} + if cache_key_args[1] not in func.__skdrivervalues[armature.name]: vals = func(*args) - func.__skdrivervalues[args[0].name][args[1]] = vals + func.__skdrivervalues[armature.name][cache_key_args[1]] = vals return vals else: - return func.__skdrivervalues[args[0].name][args[1]] + return func.__skdrivervalues[armature.name][cache_key_args[1]] return wrapper_skdrivervalues |