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.py202
1 files changed, 202 insertions, 0 deletions
diff --git a/tests/performance/api/environment.py b/tests/performance/api/environment.py
new file mode 100644
index 00000000000..bf8b893c664
--- /dev/null
+++ b/tests/performance/api/environment.py
@@ -0,0 +1,202 @@
+
+import base64
+import inspect
+import logging
+import logging.handlers
+import os
+import multiprocessing
+import pathlib
+import pickle
+import subprocess
+import sys
+from typing import Callable, Dict, List
+
+class TestEnvironment:
+ def __init__(self):
+ # Directory paths.
+ self.repo_dir = pathlib.Path(__file__).parent.parent.parent.parent
+ self.base_dir = self.repo_dir.parent / 'benchmark'
+ self.blender_dir = self.base_dir / 'blender.git'
+ self.build_dir = self.base_dir / 'build'
+ self.lib_dir = self.base_dir / 'lib'
+ self.benchmarks_dir = self.lib_dir / 'benchmarks'
+
+ # Executable paths.
+ if sys.platform == 'darwin':
+ blender_executable = 'Blender.app/Contents/MacOS/Blender'
+ elif sys.platform == 'win32':
+ blender_executable = 'blender.exe'
+ else:
+ blender_executable = 'blender'
+
+ self.blender_executable = self.build_dir / 'bin' / blender_executable
+ self.git_executable = 'git'
+ self.cmake_executable = 'cmake'
+
+ self.logger = None
+ self._init_logger()
+
+ def _init_logger(self):
+ # Logging.
+ if os.path.isdir(self.base_dir) and not self.logger:
+ log = self.base_dir / 'command.log'
+ maxbytes = 5 * 1024 * 1024
+ self.logger = logging.getLogger('Blender Benchmark')
+ self.logger.setLevel(logging.INFO)
+ handler = logging.handlers.RotatingFileHandler(log, maxBytes=maxbytes, backupCount=0)
+ self.logger.addHandler(handler)
+
+ def validate(self) -> bool:
+ benchmarks_dir = self.repo_dir.parent / 'lib' / 'benchmarks'
+ if not os.path.isdir(benchmarks_dir):
+ return 'Warning: benchmarks not found at ' + str(benchmarks_dir)
+ return None
+
+ def initialized(self) -> bool:
+ return os.path.isdir(self.base_dir) and \
+ os.path.isdir(self.blender_dir) and \
+ os.path.isdir(self.build_dir) and \
+ os.path.isdir(self.benchmarks_dir)
+
+ def init(self) -> None:
+ blender_dir = self.repo_dir
+ lib_dir = self.repo_dir.parent / 'lib'
+
+ if not os.path.isdir(self.base_dir):
+ print("Creating", self.base_dir)
+ os.makedirs(self.base_dir, exist_ok=True)
+
+ self._init_logger()
+
+ if not os.path.isdir(self.lib_dir):
+ print("Creating symlink at", self.lib_dir)
+ os.symlink(lib_dir, self.lib_dir, target_is_directory=True)
+ if not os.path.isdir(self.blender_dir):
+ print("Creating git worktree in", self.blender_dir)
+ self.call([self.git_executable, 'worktree', 'add', self.blender_dir, 'HEAD'], blender_dir)
+
+ # Setup build directory.
+ print("Configuring cmake in", self.build_dir)
+ os.makedirs(self.build_dir, exist_ok=True)
+ cmakecache = self.build_dir / 'u.txt'
+ if os.path.isfile(cmakecache):
+ os.remove(cmakecache)
+ cmake_options = ['-DWITH_CYCLES_NATIVE_ONLY=ON',
+ '-DWITH_BUILDINFO=OFF',
+ '-DWITH_INTERNATIONAL=OFF']
+ self.call([self.cmake_executable, self.blender_dir] + cmake_options, self.build_dir)
+ print("Done")
+
+ def current_revision(self) -> str:
+ lines = self.call([self.git_executable, 'rev-parse', '--short=7', 'HEAD'], self.repo_dir)
+ return lines[0].strip()
+
+ def build_revision(self, revision: str) -> None:
+ # Checkout Blender revision
+ 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, 'fetch', 'origin'], self.blender_dir)
+ self.call([self.git_executable, 'checkout', '--detach', revision], self.blender_dir)
+
+ # Update submodules not needed for now
+ # make_update = self.blender_dir / 'build_files' / 'utils' / 'make_update.py'
+ # self.call([sys.executable, make_update, '--no-libraries', '--no-blender'], self.blender_dir)
+
+ # Build
+ self.call([self.cmake_executable,
+ '--build', '.',
+ '--parallel', str(multiprocessing.cpu_count()),
+ '--target', 'install',
+ '--config', 'Release'],
+ self.build_dir)
+
+ def info(self, msg):
+ if self.logger:
+ self.logger.info(msg)
+ else:
+ print(msg)
+
+ 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."""
+ self.info("$ " + " ".join([str(arg) for arg in args]))
+ proc = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ lines = []
+ while proc.poll() is None:
+ line = proc.stdout.readline()
+ if line:
+ line = line.decode('utf-8', 'ignore')
+ self.info(line.strip())
+ lines += [line]
+
+ if proc.returncode != 0 and not silent:
+ 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']
+ 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, blendfile=None, foreground=False) -> Dict:
+ """Run function in a Blender instance. Arguments and return values are
+ passed as a dictionary that must be serializable with pickle."""
+ function_path = os.path.abspath(inspect.getfile(function))
+
+ # 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')
+
+ blender_args = []
+ if blendfile:
+ blender_args += [blendfile]
+ blender_args += ['--python-expr', expression]
+ lines = self.call_blender(blender_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
+
+ return {}
+
+ def find_blend_files(self, dirname):
+ """
+ Search for <name>.blend or <name>/<name>.blend files in the given directory
+ under lib/benchmarks.
+ """
+ dirpath = self.benchmarks_dir / dirname
+ filepaths = []
+ if os.path.isdir(dirpath):
+ for filename in os.listdir(dirpath):
+ filepath = dirpath / filename
+ if os.path.isfile(filepath) and filename.endswith('.blend'):
+ filepaths += [filepath]
+ elif os.path.isdir(filepath):
+ filepath = filepath / (filename + ".blend")
+ if os.path.isfile(filepath):
+ filepaths += [filepath]
+
+ return filepaths