diff --git a/tests/performance/tests/sculpt.py b/tests/performance/tests/sculpt.py index 47540e65cf0..7f1e337a417 100644 --- a/tests/performance/tests/sculpt.py +++ b/tests/performance/tests/sculpt.py @@ -40,7 +40,7 @@ def set_view3d_context_override(context_override): context_override["region"] = region -def prepare_sculpt_scene(context: any, mode: SculptMode): +def prepare_sculpt_scene(context: any, mode: SculptMode, subdivision_level=3): """ Prepare a clean state of the scene suitable for benchmarking @@ -97,7 +97,7 @@ def prepare_sculpt_scene(context: any, mode: SculptMode): bpy.ops.object.mode_set(mode='SCULPT') if mode == SculptMode.MULTIRES: - bpy.ops.object.subdivision_set(level=3) + bpy.ops.object.subdivision_set(level=subdivision_level) elif mode == SculptMode.DYNTOPO: bpy.ops.sculpt.dynamic_topology_toggle() @@ -220,6 +220,38 @@ def _run_bvh_test(args: dict): return sum(measurements) / len(measurements) +def _run_subdivide_test(_args: dict): + import bpy + import time + context = bpy.context + + timeout = 10 + total_time_start = time.time() + + # Create an undo stack explicitly. This isn't created by default in background mode. + bpy.ops.ed.undo_push() + + min_measurements = 5 + max_measurements = 100 + + measurements = [] + while True: + prepare_sculpt_scene(context, SculptMode.MULTIRES, subdivision_level=2) + context_override = context.copy() + set_view3d_context_override(context_override) + with context.temp_override(**context_override): + start = time.time() + bpy.ops.object.multires_subdivide(modifier="Multires") + measurements.append(time.time() - start) + + if len(measurements) >= min_measurements and (time.time() - total_time_start) > timeout: + break + if len(measurements) >= max_measurements: + break + + return sum(measurements) / len(measurements) + + class SculptBrushTest(api.Test): def __init__(self, filepath: pathlib.Path, mode: SculptMode, brush_type: BrushType): self.filepath = filepath @@ -264,10 +296,27 @@ class SculptRebuildBVHTest(api.Test): return {'time': result} +class SculptMultiresSubdivideTest(api.Test): + def __init__(self, filepath: pathlib.Path): + self.filepath = filepath + + def name(self): + return "multires_subdivide_2_to_3" + + def category(self): + return "sculpt" + + def run(self, env, _device_id): + result, _ = env.run_in_blender(_run_subdivide_test, {}, [self.filepath]) + + return {'time': result} + + def generate(env): filepaths = env.find_blend_files('sculpt/*') # For now, we only expect there to ever be a single file to use as the basis for generating other brush tests assert len(filepaths) == 1 brush_tests = [SculptBrushTest(filepaths[0], mode, brush_type) for mode in SculptMode for brush_type in BrushType] bvh_tests = [SculptRebuildBVHTest(filepaths[0], mode) for mode in SculptMode] - return brush_tests + bvh_tests + subdivision_tests = [SculptMultiresSubdivideTest(filepaths[0])] + return brush_tests + bvh_tests + subdivision_tests