From 1929b20a6c4124a345c3beb11f443759f46c74a3 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 22 Jul 2023 01:38:56 +1000 Subject: [PATCH] GSRunner: Add modal comparison Click on a dump to open. Left/right arrow keys switch between dumps. Space bar toggles between before/after. Escape closes. --- pcsx2-gsrunner/comparer.css | 97 +++++++++++++++++++++++++ pcsx2-gsrunner/comparer.js | 111 +++++++++++++++++++++++++++++ pcsx2-gsrunner/test_check_dumps.py | 30 +++++++- 3 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 pcsx2-gsrunner/comparer.css create mode 100644 pcsx2-gsrunner/comparer.js diff --git a/pcsx2-gsrunner/comparer.css b/pcsx2-gsrunner/comparer.css new file mode 100644 index 0000000000..037a8333fd --- /dev/null +++ b/pcsx2-gsrunner/comparer.css @@ -0,0 +1,97 @@ +.modal { + display: none; + position: fixed; + z-index: 1; + padding-top: 100px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: black; +} + +.modal-content { + position: relative; + margin: auto; + padding: 0; + width: 90%; + max-width: 1200px; +} + +.close { + color: white; + position: absolute; + top: 10px; + right: 25px; + font-size: 35px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: #999; + text-decoration: none; + cursor: pointer; +} + +.prev, +.next { + cursor: pointer; + position: absolute; + top: 50%; + width: auto; + padding: 16px; + margin-top: -50px; + color: white; + font-weight: bold; + font-size: 20px; + transition: 0.6s ease; + border-radius: 0 3px 3px 0; + user-select: none; + -webkit-user-select: none; +} + +.next { + right: 0; + border-radius: 3px 0 0 3px; +} + +.prev:hover, +.next:hover { + background-color: rgba(0, 0, 0, 0.8); +} + +.item img { + cursor: pointer; +} + +#compareTitle { + color: white; + font-size: 20px; + font-family: sans-serif; + margin-bottom: 10px; +} + +#compareCaption { + color: white; + font-family: sans-serif; +} + +#compareState { + display: block; + position: absolute; + right: 0; + top: 0; + color: red; + font-family: sans-serif; + font-size: 20px; + font-weight: bold; +} + +#compareImage { + display: block; + margin: 0 auto; + width: 100%; + height: auto; +} diff --git a/pcsx2-gsrunner/comparer.js b/pcsx2-gsrunner/comparer.js new file mode 100644 index 0000000000..237327d93d --- /dev/null +++ b/pcsx2-gsrunner/comparer.js @@ -0,0 +1,111 @@ +/* Worst script known to man */ +/* Sources: + https://www.w3schools.com/howto/howto_js_lightbox.asp + https://css-tricks.com/prevent-page-scrolling-when-a-modal-is-open/ +*/ + +function openModal() { + document.body.style.position = 'fixed'; + document.body.style.top = `-${window.scrollY}px`; + document.getElementById("myModal").style.display = "block"; + document.getElementById("myModal").focus(); + setImageIndex(0); +} + +function closeModal() { + document.getElementById("myModal").style.display = "none"; + const scrollY = document.body.style.top; + document.body.style.position = ''; + document.body.style.top = ''; + window.scrollTo(0, parseInt(scrollY || '0') * -1); +} + +function isModalOpen() { + return (document.getElementById("myModal").style.display == "block"); +} + +function formatLines(str) { + let lines = str.split("\n") + lines = lines.filter(line => !line.startsWith("Difference in frames")) + return lines.join("
") +} + +function extractItem(elem) { + return { + name: elem.querySelector("h1").innerText, + beforeImg: elem.querySelector(".before").getAttribute("src"), + afterImg: elem.querySelector(".after").getAttribute("src"), + details: formatLines(elem.querySelector("pre").innerText) + }; +} + +const items = [...document.querySelectorAll(".item")].map(extractItem) +let currentImage = 0; +let currentState = 0; + +function getImageIndexForUri(uri) { + for (let i = 0; i < items.length; i++) { + if (items[i].beforeImg == uri || items[i].afterImg == uri) + return i; + } + return -1; +} + +function setImageState(state) { + const item = items[currentImage] + const uri = (state === 0) ? item.beforeImg : item.afterImg; + const stateText = (state === 0) ? "BEFORE" : "AFTER"; + const posText = "(" + (currentImage + 1).toString() + "/" + (items.length).toString() + ") "; + document.getElementById("compareImage").setAttribute("src", uri); + document.getElementById("compareState").innerText = stateText; + document.getElementById("compareTitle").innerText = posText + item.name; + document.getElementById("compareCaption").innerHTML = item.details; + currentState = state; +} + +function setImageIndex(index) { + if (index < 0 || index > items.length) + return; + + currentImage = index; + setImageState(0); +} + +function handleKey(key) { + if (key == " ") { + setImageState((currentState === 0) ? 1 : 0); + return true; + } else if (key == "ArrowLeft") { + setImageIndex(currentImage - 1); + return true; + } else if (key == "ArrowRight") { + setImageIndex(currentImage + 1); + return true; + } else if (key == "Escape") { + closeModal(); + return true; + } else { + console.log(key); + return false; + } +} + +document.getElementById("myModal").addEventListener("keydown", function(ev) { + if (ev.defaultPrevented) + return; + + if (handleKey(ev.key)) + ev.preventDefault(); +}); + +document.querySelector("#myModal .prev").addEventListener("click", function() { + setImageIndex(currentImage - 1); +}); +document.querySelector("#myModal .next").addEventListener("click", function() { + setImageIndex(currentImage + 1); +}); +document.querySelectorAll(".item img").forEach(elem => elem.addEventListener("click", function() { + if (!isModalOpen()) + openModal(); + setImageIndex(getImageIndexForUri(this.getAttribute("src"))); +})); \ No newline at end of file diff --git a/pcsx2-gsrunner/test_check_dumps.py b/pcsx2-gsrunner/test_check_dumps.py index 938d79d687..d8b487c533 100644 --- a/pcsx2-gsrunner/test_check_dumps.py +++ b/pcsx2-gsrunner/test_check_dumps.py @@ -7,17 +7,35 @@ 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 = """ Comparison +""" + f"" + """ """ FILE_FOOTER = """ + +""" + f"\n" + """ """ @@ -103,6 +121,7 @@ def check_regression_test(baselinedir, testdir, name): diff_frames.append(framenum) if first_fail: + write("
") write("

{}

".format(name)) write("") first_fail = False @@ -110,14 +129,17 @@ def check_regression_test(baselinedir, testdir, name): imguri1 = Path(path1).as_uri() imguri2 = Path(path2).as_uri() write("" % (framenum)) - write("" % (imguri1, imguri2)) + write("" % (imguri1, imguri2)) + + if len(diff_frames) == MAX_DIFF_FRAMES: + break if len(diff_frames) > 0: write("
Frame %d
") - write("
Difference in frames [%s] for %s
" % (",".join(map(str, diff_frames)), name)) + write("
Difference in frames [%s] for %s%s%s
" % (",".join(map(str, diff_frames)), name, "\n" if len(stats) > 0 else "", "\n".join(stats))) + write("
") print("*** Difference in frames [%s] for %s" % (",".join(map(str, diff_frames)), name)) if len(stats) > 0: - write("
%s
" % "\n".join(stats)) print(stats) elif len(stats) > 0: write("

{}

".format(name)) @@ -149,9 +171,11 @@ 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)