Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'tests/performance/api/environment.py')
-rw-r--r--tests/performance/api/environment.py243
1 files changed, 243 insertions, 0 deletions
diff --git a/tests/performance/api/environment.py b/tests/performance/api/environment.py
new file mode 100644
index 00000000000..7c4e5e761a6
--- /dev/null
+++ b/tests/performance/api/environment.py
@@ -0,0 +1,243 @@
+# Apache License, Version 2.0
+
+import base64
+import glob
+import inspect
+import multiprocessing
+import os
+import pathlib
+import platform
+import pickle
+import subprocess
+import sys
+from typing import Callable, Dict, List
+
+from .config import TestConfig
+from .device import TestMachine
+
+class TestEnvironment:
+ def __init__(self, blender_git_dir: pathlib.Path, base_dir: pathlib.Path):
+ self.blender_git_dir = blender_git_dir
+ self.base_dir = base_dir
+ self.blender_dir = base_dir / 'blender'
+ self.build_dir = base_dir / 'build'
+ self.lib_dir = base_dir / 'lib'
+ self.benchmarks_dir = self.blender_git_dir.parent / 'lib' / 'benchmarks'
+ self.git_executable = 'git'
+ self.cmake_executable = 'cmake'
+ self.cmake_options = ['-DWITH_INTERNATIONAL=OFF', '-DWITH_BUILDINFO=OFF']
+ self.unset_blender_executable()
+ self.log_file = None
+ self.machine = None
+
+ def get_machine(self, need_gpus: bool=True) -> None:
+ if not self.machine or (need_gpus and not self.machine.has_gpus):
+ self.machine = TestMachine(self, need_gpus)
+
+ return self.machine
+
+ def init(self, build) -> None:
+ if not self.benchmarks_dir.exists():
+ sys.stderr.write(f'Error: benchmark files directory not found at {self.benchmarks_dir}')
+ sys.exit(1)
+
+ # Create benchmarks folder contents.
+ print(f'Init {self.base_dir}')
+ self.base_dir.mkdir(parents=True, exist_ok=True)
+
+ if len(self.get_configs(names_only=True)) == 0:
+ config_dir = self.base_dir / 'default'
+ print(f'Creating default configuration in {config_dir}')
+ TestConfig.write_default_config(self, config_dir)
+
+ if build:
+ if not self.lib_dir.exists():
+ print(f'Creating symlink at {self.lib_dir}')
+ self.lib_dir.symlink_to(self.blender_git_dir.parent / 'lib')
+ else:
+ print(f'Exists {self.lib_dir}')
+
+ if not self.blender_dir.exists():
+ print(f'Init git worktree in {self.blender_dir}')
+ self.call([self.git_executable, 'worktree', 'add', '--detach', self.blender_dir, 'HEAD'], self.blender_git_dir)
+ else:
+ print(f'Exists {self.blender_dir}')
+
+ if not self.build_dir.exists():
+ print(f'Init build in {self.build_dir}')
+ self.build_dir.mkdir()
+ # No translation to avoid dealing with submodules
+ self.call([self.cmake_executable, self.blender_dir, '.'] + self.cmake_options, self.build_dir)
+ else:
+ print(f'Exists {self.build_dir}')
+
+ print("Building")
+ self.build()
+
+ print('Done')
+
+ def checkout(self) -> None:
+ # Checkout Blender revision
+ if not self.blender_dir.exists():
+ sys.stderr.write('\n\nError: no build set up, run `./benchmark init --build` first\n')
+ sys.exit(1)
+
+ self.call([self.git_executable, 'clean', '-f', '-d'], self.blender_dir)
+ self.call([self.git_executable, 'reset', '--hard', 'HEAD'], self.blender_dir)
+ self.call([self.git_executable, 'checkout', '--detach', git_hash], self.blender_dir)
+
+ self.build()
+
+ def build(self) -> None:
+ # Build Blender revision
+ if not self.build_dir.exists():
+ sys.stderr.write('\n\nError: no build set up, run `./benchmark init --build` first\n')
+ sys.exit(1)
+
+ jobs = str(multiprocessing.cpu_count())
+ self.call([self.cmake_executable, '.'] + self.cmake_options, self.build_dir)
+ self.call([self.cmake_executable, '--build', '.', '-j', jobs, '--target', 'install'], self.build_dir)
+
+ def set_blender_executable(self, executable_path: pathlib.Path) -> None:
+ # Run all Blender commands with this executable.
+ self.blender_executable = executable_path
+
+ def unset_blender_executable(self) -> None:
+ if platform.system() == "Windows":
+ self.blender_executable = self.build_dir / 'bin' / 'blender.exe'
+ elif platform.system() == "Darwin":
+ self.blender_executable = self.build_dir / 'bin' / 'Blender.app' / 'Contents' / 'MacOS' / 'Blender'
+ else:
+ self.blender_executable = self.build_dir / 'bin' / 'blender'
+
+ if not self.blender_executable.exists():
+ self.blender_executable = 'blender'
+
+ def set_log_file(self, filepath: pathlib.Path, clear=True) -> None:
+ # Log all commands and output to this file.
+ self.log_file = filepath
+
+ if clear:
+ self.log_file.unlink(missing_ok=True)
+
+ def unset_log_file(self) -> None:
+ self.log_file = None
+
+ def call(self, args: List[str], cwd: pathlib.Path, silent=False) -> List[str]:
+ # Execute command with arguments in specified directory,
+ # and return combined stdout and stderr output.
+
+ # Open log file for writing
+ f = None
+ if self.log_file:
+ if not self.log_file.exists():
+ self.log_file.parent.mkdir(parents=True, exist_ok=True)
+ f = open(self.log_file, 'a')
+ f.write('\n' + ' '.join([str(arg) for arg in args]) + '\n\n')
+
+ proc = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ # Read line by line
+ lines = []
+ try:
+ while proc.poll() is None:
+ line = proc.stdout.readline()
+ if line:
+ line_str = line.decode('utf-8', 'ignore')
+ lines.append(line_str)
+ if f:
+ f.write(line_str)
+ except KeyboardInterrupt:
+ # Avoid processes that keep running when interrupting.
+ proc.terminate()
+
+ if f:
+ f.close()
+
+ # Print command output on error
+ if proc.returncode != 0 and not silent:
+ for line in lines:
+ print(line.rstrip())
+ raise Exception("Error executing command")
+
+ return lines
+
+ def call_blender(self, args: List[str], foreground=False) -> List[str]:
+ # Execute Blender command with arguments.
+ common_args = ['--factory-startup', '--enable-autoexec', '--python-exit-code', '1']
+ if foreground:
+ common_args += ['--no-window-focus', '--window-geometry', '0', '0', '1024', '768']
+ else:
+ common_args += ['--background']
+
+ return self.call([self.blender_executable] + common_args + args, cwd=self.base_dir)
+
+ def run_in_blender(self,
+ function: Callable[[Dict], Dict],
+ args: Dict,
+ blender_args: List=[],
+ foreground=False) -> Dict:
+ # Run function in a Blender instance. Arguments and return values are
+ # passed as a Python object that must be serializable with pickle.
+
+ # Get information to call this function from Blender.
+ package_path = pathlib.Path(__file__).parent.parent
+ functionname = function.__name__
+ modulename = inspect.getmodule(function).__name__
+
+ # Serialize arguments in base64, to avoid having to escape it.
+ args = base64.b64encode(pickle.dumps(args))
+ output_prefix = 'TEST_OUTPUT: '
+
+ expression = (f'import sys, pickle, base64\n'
+ f'sys.path.append("{package_path}")\n'
+ f'import {modulename}\n'
+ f'args = pickle.loads(base64.b64decode({args}))\n'
+ f'result = {modulename}.{functionname}(args)\n'
+ f'result = base64.b64encode(pickle.dumps(result))\n'
+ f'print("{output_prefix}" + result.decode())\n')
+
+ expr_args = blender_args + ['--python-expr', expression]
+ lines = self.call_blender(expr_args, foreground=foreground)
+
+ # Parse output.
+ for line in lines:
+ if line.startswith(output_prefix):
+ output = line[len(output_prefix):].strip()
+ result = pickle.loads(base64.b64decode(output))
+ return result, lines
+
+ return {}, lines
+
+ def find_blend_files(self, dirpath: pathlib.Path) -> List:
+ # Find .blend files in subdirectories of the given directory in the
+ # lib/benchmarks directory.
+ dirpath = self.benchmarks_dir / dirpath
+ filepaths = []
+ for filename in glob.iglob(str(dirpath / '*.blend'), recursive=True):
+ filepaths.append(pathlib.Path(filename))
+ return filepaths
+
+ def get_configs(self, name: str=None, names_only: bool=False) -> List:
+ # Get list of configurations in the benchmarks directory.
+ configs = []
+
+ if self.base_dir.exists():
+ for dirname in os.listdir(self.base_dir):
+ if not name or dirname == name:
+ dirpath = self.base_dir / dirname / 'config.py'
+ if dirpath.exists():
+ if names_only:
+ configs.append(dirname)
+ else:
+ configs.append(TestConfig(self, dirname))
+
+ return configs
+
+ def resolve_git_hash(self, revision):
+ # Get git hash for a tag or branch.
+ return self.call([self.git_executable, 'rev-parse', revision], self.blender_git_dir)[0].strip()
+
+ def git_hash_date(self, git_hash):
+ # Get commit data for a git hash.
+ return int(self.call([self.git_executable, 'log', '-n1', git_hash, '--format=%at'], self.blender_git_dir)[0].strip())