Files
archived-pcsx2/pcsx2-gsrunner/test_check_dumps.py
Stenzek 3e8e1ed596 GS/HW: Don't align dirty rectangles to block sizes when updating
Don't align the area we write to the target to the block size. If the format matches, the writes don't need
to be block aligned. We still read the whole thing in, because that's the granularity that ReadTexture
operates at, but discard those pixels when updating the framebuffer. Onimusha 2 does this dance where it
uploads the left 4 pixels to the middle of the image, then moves it to the left, and because we process
the move in hardware, local memory never gets updated, and thus is stale.
2023-10-18 01:08:34 +10:00

191 lines
5.8 KiB
Python

import argparse
import glob
import sys
import os
import re
import hashlib
from pathlib import Path
MAX_DIFF_FRAMES = 9999
SCRIPTDIR = os.path.abspath(os.path.dirname(__file__))
CSS_PATH = Path(os.path.join(SCRIPTDIR, "comparer.css")).as_uri()
JS_PATH = Path(os.path.join(SCRIPTDIR, "comparer.js")).as_uri()
FILE_HEADER = """
<!DOCTYPE html>
<html>
<head>
<title>Comparison</title>
""" + f"<link rel=\"stylesheet\" href=\"{CSS_PATH}\" />" + """
</head>
<body>
"""
FILE_FOOTER = """
<div id="myModal" class="modal" tabindex="0">
<span class="close cursor" onclick="closeModal()">&times;</span>
<div class="modal-content">
<div id="compareTitle"></div>
<img id="compareImage" />
<div id="compareState"></div>
<a class="prev">&#10094;</a>
<a class="next">&#10095;</a>
<div id="compareCaption"></div>
</div>
</div>
""" + f"<script src=\"{JS_PATH}\"></script>\n" + """
</body>
</html>
"""
outfile = None
def write(line):
outfile.write(line + "\n")
def compare_frames(path1, path2):
try:
with open(path1, "rb") as f:
hash1 = hashlib.md5(f.read()).digest()
with open(path2, "rb") as f:
hash2 = hashlib.md5(f.read()).digest()
return hash1 == hash2
except (FileNotFoundError, IOError):
return False
def extract_stats(file):
stats = {}
try:
with open(file, "r", errors="ignore") as f:
for line in f.readlines():
m = re.match(".*@HWSTAT@ ([^:]+): (.*) \(avg ([^)]+)\)$", line)
if m is None:
continue
stats[m[1]] = int(m[3])
except FileNotFoundError:
pass
except IOError:
pass
return stats
def compare_stats(baselinedir, testdir):
stats1 = extract_stats(os.path.join(baselinedir, "emulog.txt"))
stats2 = extract_stats(os.path.join(testdir, "emulog.txt"))
res = []
for statname in stats1.keys():
if statname not in stats2 or stats1[statname] == stats2[statname]:
continue
v2 = stats2[statname]
v1 = stats1[statname]
delta = v2 - v1
res.append("%s: %s%d [%d=>%d]" % (statname, "+" if delta > 0 else "", delta, v1, v2))
return res
def check_regression_test(baselinedir, testdir, name):
#print("Checking '%s'..." % name)
dir1 = os.path.join(baselinedir, name)
dir2 = os.path.join(testdir, name)
if not os.path.isdir(dir2):
#print("*** %s is missing in test set" % name)
return False
images = glob.glob(os.path.join(dir1, "*_frame*.png"))
diff_frames = []
first_fail = True
stats = compare_stats(dir1, dir2)
for imagepath in images:
imagename = Path(imagepath).name
matches = re.match(".*_frame([0-9]+).png", imagename)
if matches is None:
continue
framenum = int(matches[1])
path1 = os.path.join(dir1, imagename)
path2 = os.path.join(dir2, imagename)
if not os.path.isfile(path2):
print("--- Frame %u for %s is missing in test set" % (framenum, name))
write("<h1>{}</h1>".format(name))
write("--- Frame %u for %s is missing in test set" % (framenum, name))
return False
if not compare_frames(path1, path2):
diff_frames.append(framenum)
if first_fail:
write("<div class=\"item\">")
write("<h1>{}</h1>".format(name))
write("<table width=\"100%\">")
first_fail = False
imguri1 = Path(path1).as_uri()
imguri2 = Path(path2).as_uri()
write("<tr><td colspan=\"2\">Frame %d</td></tr>" % (framenum))
write("<tr><td><img class=\"before\" src=\"%s\" /></td><td><img class=\"after\" src=\"%s\" /></td></tr>" % (imguri1, imguri2))
if len(diff_frames) == MAX_DIFF_FRAMES:
break
if len(diff_frames) > 0:
write("</table>")
write("<pre>Difference in frames [%s] for %s%s%s</pre>" % (",".join(map(str, diff_frames)), name, "\n" if len(stats) > 0 else "", "\n".join(stats)))
write("</div>")
print("*** Difference in frames [%s] for %s" % (",".join(map(str, diff_frames)), name))
if len(stats) > 0:
print(stats)
elif len(stats) > 0:
write("<h1>{}</h1>".format(name))
write("<pre>%s</pre>" % "\n".join(stats))
print(name, stats)
return len(diff_frames) == 0
def check_regression_tests(baselinedir, testdir):
gamedirs = glob.glob(baselinedir + "/*", recursive=False)
success = 0
failure = 0
for gamedir in gamedirs:
name = Path(gamedir).name
if check_regression_test(baselinedir, testdir, name):
success += 1
else:
failure += 1
print("%d dumps unchanged" % success)
print("%d dumps changed" % failure)
return (failure == 0)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Check frame dump images for regression tests")
parser.add_argument("-baselinedir", action="store", required=True, help="Directory containing baseline frames to check against")
parser.add_argument("-testdir", action="store", required=True, help="Directory containing frames to check")
parser.add_argument("-maxframes", type=int, action="store", required=False, default=9999, help="Max frames to compare")
parser.add_argument("outfile", action="store", help="The file to write the output to")
args = parser.parse_args()
MAX_DIFF_FRAMES = args.maxframes
outfile = open(args.outfile, "w")
write(FILE_HEADER)
if not check_regression_tests(os.path.realpath(args.baselinedir), os.path.realpath(args.testdir)):
write(FILE_FOOTER)
outfile.close()
sys.exit(1)
else:
outfile.close()
os.remove(args.outfile)
sys.exit(0)