mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
78 lines
3.0 KiB
Python
78 lines
3.0 KiB
Python
"""Smoke test for build/xlate/totk_xlate(.exe): no game files needed.
|
|
|
|
Sends one hand-assembled Maxwell fragment shader (writes the constant colour 1.0, 0.5, 0.25, 1.0 to render target 0)
|
|
through --serve, checks the reply, validates the SPIR-V with spirv-val, then sends a malformed line and checks the
|
|
service survives and still answers.
|
|
|
|
python tools/xlate_smoke.py
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import shutil
|
|
import struct
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
EXE = ROOT / "build" / "xlate" / ("totk_xlate.exe" if os.name == "nt" else "totk_xlate")
|
|
SPIRV_VAL = (Path(r"C:\msys64\ucrt64\bin\spirv-val.exe") if os.name == "nt"
|
|
else Path(shutil.which("spirv-val") or "/usr/bin/spirv-val"))
|
|
|
|
PT = 7 << 16 # predicate "always"
|
|
|
|
|
|
def mov32i(dest: int, value: float) -> int:
|
|
imm = struct.unpack("<I", struct.pack("<f", value))[0]
|
|
return 0x0100000000000000 | (imm << 20) | (0xF << 12) | PT | dest
|
|
|
|
|
|
EXIT = 0xE30000000000000F | PT
|
|
SCHED = 0x001F8000FC0007E0 # scheduling control word; the decoder skips it
|
|
|
|
|
|
def fragment_code() -> bytes:
|
|
header = [0] * 20
|
|
header[0] = 2 | (3 << 5) | (5 << 10) # SPH type 2, version 3, stage fragment
|
|
header[18] = 0xF # OMAP: render target 0, components xyzw
|
|
words = [SCHED, mov32i(0, 1.0), mov32i(1, 0.5), mov32i(2, 0.25),
|
|
SCHED, mov32i(3, 1.0), EXIT, 0]
|
|
return struct.pack("<20I", *header) + struct.pack(f"<{len(words)}Q", *words)
|
|
|
|
|
|
def main() -> int:
|
|
if not EXE.is_file():
|
|
print(f"missing {EXE}; run tools/build_xlate.py", file=sys.stderr)
|
|
return 2
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
tmp = Path(tmp)
|
|
code = tmp / "ps.bin"
|
|
code.write_bytes(fragment_code())
|
|
out = tmp / "out"
|
|
job = {"key": "const_ps", "stage": "ps", "code": str(code), "debug": False, "fout": {"0": "float"}}
|
|
lines = [
|
|
json.dumps({"job": job, "out": str(out)}),
|
|
"this is not json",
|
|
json.dumps({"job": dict(job, key="again"), "out": str(out)}),
|
|
]
|
|
r = subprocess.run([str(EXE), "--serve"], input="\n".join(lines) + "\n", capture_output=True, text=True, timeout=60)
|
|
replies = r.stdout.splitlines()
|
|
print("replies:", replies)
|
|
if len(replies) != 3 or replies[0] != "ok const_ps" or not replies[1].startswith("fail ") or replies[2] != "ok again":
|
|
print("unexpected replies; stderr:", r.stderr, file=sys.stderr)
|
|
return 1
|
|
spv = out / "const_ps.spv"
|
|
data = spv.read_bytes()
|
|
if len(data) < 20 or struct.unpack("<I", data[:4])[0] != 0x07230203:
|
|
print("reply is not SPIR-V", file=sys.stderr)
|
|
return 1
|
|
v = subprocess.run([str(SPIRV_VAL), "--target-env", "vulkan1.3", str(spv)], capture_output=True, text=True)
|
|
print(f"spirv-val exit {v.returncode} ({len(data)} bytes) {v.stdout}{v.stderr}")
|
|
return v.returncode
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|