mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-10-26 21:17:40 +00:00
105 lines
5.2 KiB
Python
105 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Compare how fast Raven and Wine present Direct3D 11 frames from the same program on the same compositor and driver.
|
|
|
|
testdata/d3d11/present_rate.exe draws a triangle into a swap chain and presents it without waiting for the display, 2000 times after a
|
|
warm-up, timing those presents with the performance counter; it writes the frame count and the microseconds they took. That gives the
|
|
presentation rate of the whole path (DXVK, the Vulkan driver and the compositor) for a 256 by 256 window. Each runtime runs REPEATS times
|
|
on its own isolated headless compositor and the table shows the median.
|
|
|
|
usage: benchmark-frames.py RAVEN WINE TESTDATA DXVK_PE64_DIRECTORY WORKDIR [REPEATS]
|
|
"""
|
|
|
|
from pathlib import Path
|
|
import json
|
|
import os
|
|
import shutil
|
|
import statistics
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
|
|
def run_once(runtime, raven, wine, program, prefix, work, index):
|
|
runtime_dir = Path(f"/run/user/{os.getuid()}/raven-labwc-bench")
|
|
runtime_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
for name in ("wayland-0", "wayland-0.lock"):
|
|
(runtime_dir / name).unlink(missing_ok=True)
|
|
env = {"PATH": "/usr/bin:/bin", "HOME": os.environ["HOME"], "TMPDIR": os.environ.get("TMPDIR", str(work)), "XDG_RUNTIME_DIR": str(runtime_dir),
|
|
"WAYLAND_DISPLAY": "wayland-0", "VK_DRIVER_FILES": "/usr/share/vulkan/icd.d/lvp_icd.json", "LP_NUM_THREADS": "2"}
|
|
compositor = subprocess.Popen(["labwc"], env=env | {"WLR_BACKENDS": "headless", "WLR_RENDERER": "pixman", "WLR_HEADLESS_OUTPUTS": "1"},
|
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
try:
|
|
for _ in range(100):
|
|
if (runtime_dir / "wayland-0").exists():
|
|
break
|
|
time.sleep(0.1)
|
|
time.sleep(0.5)
|
|
if runtime == "raven":
|
|
env["RAVEN_PREFIX"] = str(prefix)
|
|
command = [str(raven), "--run-process", "--execution=native", "--timeout-ms=100000", "--desktop=wayland", str(program)]
|
|
else:
|
|
env |= {"WINEPREFIX": str(prefix), "WINEDEBUG": "-all", "WINEDLLOVERRIDES": "mscoree=;mshtml=;d3d11,dxgi,d3d10core=n"}
|
|
command = [str(wine), str(program)]
|
|
marker = prefix / "drive_c" / "present_rate.txt"
|
|
marker.unlink(missing_ok=True)
|
|
process = subprocess.Popen(command, env=env, cwd=program.parent, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
try:
|
|
deadline = time.time() + float(os.environ.get("RAVEN_BENCH_TIMEOUT", "120"))
|
|
while time.time() < deadline and process.poll() is None and not marker.exists():
|
|
time.sleep(0.05)
|
|
time.sleep(0.2)
|
|
fields = marker.read_text().split() if marker.exists() else []
|
|
if len(fields) != 2 or not fields[0].isdigit():
|
|
return None
|
|
return int(fields[0]) / (int(fields[1]) / 1e6)
|
|
finally:
|
|
process.terminate()
|
|
try:
|
|
process.wait(10)
|
|
except subprocess.TimeoutExpired:
|
|
process.kill()
|
|
if runtime == "wine":
|
|
subprocess.run([str(wine.with_name("wineserver") if wine.with_name("wineserver").exists() else "/usr/lib/x86_64-linux-gnu/wine/wineserver"), "-k"],
|
|
env=env, capture_output=True, timeout=60)
|
|
finally:
|
|
compositor.terminate()
|
|
compositor.wait(10)
|
|
|
|
|
|
def main():
|
|
raven, wine, testdata, dxvk, work = (Path(value).resolve() for value in sys.argv[1:6])
|
|
repeats = int(sys.argv[6]) if len(sys.argv) > 6 else 3
|
|
shutil.rmtree(work, ignore_errors=True)
|
|
work.mkdir(parents=True)
|
|
program_dir = work / "program"
|
|
program_dir.mkdir()
|
|
for name in ("d3d11.dll", "dxgi.dll", "d3d10core.dll"):
|
|
shutil.copy(dxvk / name, program_dir / name)
|
|
shutil.copy(testdata / "d3d11" / "present_rate.exe", program_dir / "present_rate.exe")
|
|
warm = work / "wine-warm"
|
|
warm_env = {"PATH": "/usr/bin:/bin", "HOME": os.environ["HOME"], "WINEPREFIX": str(warm), "WINEDEBUG": "-all"}
|
|
subprocess.run([str(wine), "wineboot", "-u"], env=warm_env, capture_output=True, timeout=300)
|
|
server = wine.with_name("wineserver")
|
|
if not server.exists():
|
|
server = Path("/usr/lib/x86_64-linux-gnu/wine/wineserver")
|
|
subprocess.run([str(server), "-k"], env=warm_env, capture_output=True, timeout=60)
|
|
rates = {"raven": [], "wine": []}
|
|
measurements = []
|
|
for runtime in ("raven", "wine"):
|
|
for index in range(repeats):
|
|
prefix = work / f"prefix-{runtime}-{index}"
|
|
if runtime == "wine":
|
|
shutil.copytree(warm, prefix, symlinks=True)
|
|
rate = run_once(runtime, raven, wine, program_dir / "present_rate.exe", prefix, work, index)
|
|
measurements.append(dict(runtime=runtime, trial=index + 1, frames_per_second=rate))
|
|
if rate:
|
|
rates[runtime].append(rate)
|
|
(work / "samples.json").write_text(json.dumps(measurements, indent=2) + "\n")
|
|
print(f"{'runtime':8} {'frames per second':>18} runs")
|
|
for runtime, values in rates.items():
|
|
print(f"{runtime:8} {statistics.median(values):18.1f} {len(values)}/{repeats}" if values else f"{runtime:8} {'-':>18} 0/{repeats}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|