The thing is, due to possibly NUMA or CPU groups used, number of Blender threads might be different from system number of threads.
167 lines
5.9 KiB
Python
167 lines
5.9 KiB
Python
import os
|
|
import time
|
|
import subprocess
|
|
|
|
from foundation import (logger,
|
|
progress,
|
|
stats,
|
|
util)
|
|
|
|
|
|
def constructBenchmarkCommand(ctx, scene, blendfile, output_folder, cfra):
|
|
command = [ctx.blender,
|
|
"--background",
|
|
"--factory-startup",
|
|
"-noaudio",
|
|
"--debug-cycles",
|
|
"--enable-autoexec",
|
|
"--engine", "CYCLES",
|
|
blendfile]
|
|
if ctx.image_output_dir:
|
|
output = os.path.join(ctx.image_output_dir, scene) + "#"
|
|
command.extend(["--render-format", "PNG",
|
|
"--render-output", output])
|
|
else:
|
|
command.extend(["--render-output", output_folder,
|
|
"--render-format", "PNG"])
|
|
command.extend(["--python", ctx.configure_script,
|
|
"-f", str(cfra),
|
|
"--", "--benchmark-device-type", ctx.device_type])
|
|
if ctx.device_name:
|
|
command.extend(["--benchmark-device", ctx.device_name])
|
|
return command
|
|
|
|
|
|
def benchmarkBlenderWatched(command):
|
|
# Run Blender with configured command line.
|
|
logger.DEBUG("About to execuet command: {}" . format(command))
|
|
start_time = time.time()
|
|
process = subprocess.Popen(command,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT)
|
|
|
|
# Keep reading status while Blender is alive.
|
|
st = stats.Stats()
|
|
while True:
|
|
line = process.stdout.readline()
|
|
if line == b"" and process.poll() is not None:
|
|
break
|
|
line = line.decode().strip()
|
|
if line == "":
|
|
continue
|
|
if logger.VERBOSE:
|
|
print(line)
|
|
else:
|
|
logger.DEBUG("Line from stdout: {}" . format(line))
|
|
st.update(line)
|
|
if st.current_tiles != 0:
|
|
elapsed_time = time.time() - start_time
|
|
elapsed_time_str = util.humanReadableTimeDifference(elapsed_time)
|
|
progress.progress(st.current_tiles,
|
|
st.total_tiles,
|
|
prefix="Path Tracing Tiles {}" .
|
|
format(elapsed_time_str))
|
|
|
|
# Clear line used by progress.
|
|
progress.progressClear()
|
|
|
|
if process.returncode != 0:
|
|
logger.ERROR("Rendering crashed")
|
|
return None
|
|
logger.OK("Successfully rendered")
|
|
|
|
return st
|
|
|
|
|
|
def benchmarkScene(ctx, scene):
|
|
logger.BOLD("Begin benchmark of scene {}" . format(scene))
|
|
# Get usable full path to the corresponding .blend file.
|
|
blendfile = ctx.getSceneFilename(scene)
|
|
logger.DEBUG("File to use: {}" . format(blendfile))
|
|
# Get command for rendering.
|
|
# TODO(sergey): Create some temp folder.
|
|
cfra = util.queryCurrentFrame(blendfile)
|
|
command = constructBenchmarkCommand(ctx, scene, blendfile, "/tmp/", cfra)
|
|
logger.DEBUG("Command for rendering: {}" . format(command))
|
|
|
|
logger.INFO("> Warm-up round, making sure everything is ready " +
|
|
"(this might take several minutes).")
|
|
warmup_command = command + ['--benchmark-warmup']
|
|
benchmarkBlenderWatched(warmup_command)
|
|
# Remove resutl of warmup round.
|
|
if ctx.image_output_dir:
|
|
full_image_output = os.path.join(ctx.image_output_dir, scene) + \
|
|
str(cfra) + ".png"
|
|
if os.path.exists(full_image_output):
|
|
os.remove(full_image_output)
|
|
# TODO(sergey): Consider doing several passes.
|
|
logger.INFO("> Doing real benchmark pass now.")
|
|
stats = benchmarkBlenderWatched(command)
|
|
# Rename file to more sensible name.
|
|
if ctx.image_output_dir:
|
|
if os.path.exists(full_image_output):
|
|
full_image_output_no_frame = \
|
|
os.path.join(ctx.image_output_dir, scene) + ".png"
|
|
os.rename(full_image_output, full_image_output_no_frame)
|
|
if stats:
|
|
logger.INFO("Total render time: {}" . format(
|
|
util.humanReadableTimeDifference(
|
|
stats.pipeline_render_time)))
|
|
return stats
|
|
|
|
|
|
def benchmarkAll(ctx):
|
|
"""
|
|
Benchmark all scenes from the cntext with requested settings.
|
|
"""
|
|
# First of all, print summary of what we'll be doing.
|
|
ctx.printSummary()
|
|
if not ctx.verify():
|
|
return False
|
|
all_stats = {}
|
|
for scene in ctx.scenes:
|
|
file_stats = benchmarkScene(ctx, scene)
|
|
all_stats[scene] = file_stats
|
|
return all_stats
|
|
|
|
|
|
def benchmarkGetDeviceInfo(ctx):
|
|
command = [ctx.blender,
|
|
"--background",
|
|
"--factory-startup",
|
|
"-noaudio",
|
|
"--enable-autoexec",
|
|
"--engine", "CYCLES",
|
|
"--python", ctx.configure_script,
|
|
"--",
|
|
"--benchmark-device-type", ctx.device_type]
|
|
if ctx.device_name:
|
|
command.extend(["--benchmark-device", ctx.device_name])
|
|
process = subprocess.Popen(command,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT)
|
|
stdout, stderr = process.communicate()
|
|
lines = stdout.decode().split("\n")
|
|
# Parse output
|
|
device_type = ""
|
|
compute_devices = []
|
|
num_cpu_threads = 0
|
|
for line in lines:
|
|
if line.startswith("Compute device type:"):
|
|
device_type = line.split(':', 1)[1].strip()
|
|
elif line.startswith("Using compute device:"):
|
|
compute_devices.append(line.split(':', 1)[1].strip())
|
|
elif line.startswith("CPU threads used:"):
|
|
num_cpu_threads = int(line.split(':', 1)[1].strip())
|
|
return {"device_type": device_type,
|
|
"compute_devices": compute_devices,
|
|
"num_cpu_threads": num_cpu_threads}
|
|
|
|
|
|
def benchmarkPrintDeviceInfo(ctx):
|
|
device_info = benchmarkGetDeviceInfo(ctx)
|
|
logger.INFO(" Device type: {}" . format(device_info["device_type"]))
|
|
logger.INFO(" Compute devices:")
|
|
for compute_device in device_info["compute_devices"]:
|
|
logger.INFO(" {}" . format(compute_device))
|