Files
markbench-tests/blender_render/blender_utils.py
2025-10-21 17:44:36 -07:00

145 lines
5.3 KiB
Python

"""Blender render test script"""
from datetime import datetime
import logging
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
from dataclasses import dataclass
# pylint: disable=no-name-in-module
from win32api import LOWORD, HIWORD, GetFileVersionInfo
sys.path.insert(1, os.path.join(sys.path[0], '..'))
from harness_utils.misc import download_file, extract_file_from_archive
SCRIPT_DIR = Path(__file__).resolve().parent
@dataclass
class BlenderScene:
"""a renderable blender project"""
name: str
file_name: str
download_url: str
BENCHMARK_CONFIG = {
"Barbershop": BlenderScene(
name="barbershop",
file_name="barbershop_interior.blend",
download_url="https://svn.blender.org/svnroot/bf-blender/trunk/lib/benchmarks/cycles/barbershop_interior/barbershop_interior.blend"
),
"Monster": BlenderScene(
name="monster",
file_name="monster_under_the_bed_sss_demo_by_metin_seven.blend",
download_url="https://download.blender.org/demo/cycles/monster_under_the_bed_sss_demo_by_metin_seven.blend"
),
"Junkshop": BlenderScene(
name="junkshop",
file_name="Junkshop.blend",
download_url="https://svn.blender.org/svnroot/bf-blender/tags/blender-4.1-release/lib/benchmarks/cycles/junkshop/junkshop.blend"
),
"BMW": BlenderScene(
name="bmw",
file_name="bmw27_cpu.blend",
download_url="https://download.blender.org/demo/test/BMW27_2.blend.zip"
)
}
def download_scene(scene: BlenderScene) -> None:
"""download blender project to script directory, tries network drive then the internet"""
destination = SCRIPT_DIR.joinpath(scene.file_name)
if destination.exists():
logging.info("%s scene file detected, no downloading required", scene.file_name)
return
try:
copy_scene_from_network_drive(scene.file_name, destination)
if destination.exists():
return
except Exception as ex:
logging.warning(ex)
logging.warning("could not download from network drive...")
try:
logging.info("downloading %s from internet...", scene.file_name)
download_file(scene.download_url, destination)
if scene.name == "bmw":
extract_file_from_archive(destination, "bmw27/bmw27_cpu.blend", SCRIPT_DIR)
if destination.exists():
return
except Exception as ex:
logging.error("could not download scene from any source, check connections and try again")
raise Exception("error downloading scene", cause=ex) from ex
def copy_scene_from_network_drive(file_name, destination):
"""copy blend file from network drive"""
network_dir = Path("\\\\labs.lmg.gg\\labs\\03_ProcessingFiles\\Blender Render")
source_path = network_dir.joinpath(file_name)
logging.info("Copying %s from %s", file_name, source_path)
shutil.copyfile(source_path, destination)
def time_to_seconds(time_string):
"""convert string to duration in seconds"""
colon_count = time_string.count(':')
time_format = "%H:%M:%S.%f"
if colon_count < 2:
time_format = "%M:%S.%f"
time_obj = datetime.strptime(time_string, time_format)
seconds = (time_obj.hour * 3600) + (time_obj.minute * 60) + time_obj.second + (time_obj.microsecond / 1e6)
return seconds
def run_blender_render(executable_path: Path, log_directory: Path, device: str,
benchmark: BlenderScene) -> str:
"""Execute the blender render of barbershop, returns the duration as string"""
blend_log = log_directory.joinpath("blender.log")
blend_path = SCRIPT_DIR.joinpath(benchmark.file_name)
cmd_line = f'"{str(executable_path)}" -b -E CYCLES -y "{str(blend_path)}" -f 1 -- --cycles-device {device} --cycles-print-stats'
with open(blend_log, 'w', encoding="utf-8") as f_obj:
subprocess.run(cmd_line, stdout=f_obj, text=True, check=True)
# example: Time: 02:59.57 (Saving: 00:00.16)
time_regex = r".*Time:\s+([\d:.]+)\s+\(Saving.*\)"
time = None
with open(blend_log, 'r', encoding="utf-8") as file:
lines = file.readlines()
lines.reverse()
count = 0
for line in lines:
count += 1
match = re.match(time_regex, line.strip())
if match:
time = match.group(1)
break
return time_to_seconds(time)
def find_blender():
"""Find installed blender and return path and version"""
blender_dir = Path("C:\\Program Files\\Blender Foundation\\")
versions = []
if not blender_dir.exists():
raise Exception("Blender not detected")
for directory in os.listdir(blender_dir):
# expecting subdir following pattern of Blender 3.6, Blender 3.5, etc.
versions.append(directory.replace("Blender", "").strip())
versions.sort(reverse=True)
latest_ver = versions[0]
executable_path = blender_dir.joinpath(f"Blender {latest_ver}", "blender.exe")
if not executable_path.exists():
raise Exception("Blender not detected")
info = GetFileVersionInfo(str(executable_path), "\\")
version_ms = info['FileVersionMS']
version_ls = info['FileVersionLS']
version = f"{HIWORD (version_ms)}.{LOWORD (version_ms)}.{HIWORD (version_ls)}.{LOWORD (version_ls)}"
return executable_path, version