diff --git a/gltf/basisenc.cpp b/gltf/basisenc.cpp index c1080949..c415d2c5 100644 --- a/gltf/basisenc.cpp +++ b/gltf/basisenc.cpp @@ -26,15 +26,15 @@ struct BasisSettings }; static const BasisSettings kBasisSettings[10] = { - {1, 1, 0, 1.5f}, - {1, 6, 0, 1.f}, - {1, 20, 1, 1.0f}, - {1, 50, 1, 0.75f}, - {1, 90, 1, 0.5f}, - {1, 128, 1, 0.4f}, - {1, 160, 1, 0.34f}, - {1, 192, 1, 0.29f}, // default - {1, 224, 2, 0.26f}, + {1, 1, 0, 4.f}, + {1, 32, 0, 3.f}, + {1, 64, 1, 2.f}, + {1, 96, 1, 1.5f}, + {1, 128, 1, 1.f}, // quality arguments aligned with basisu defaults + {1, 150, 1, 0.8f}, + {1, 170, 1, 0.6f}, + {1, 192, 1, 0.4f}, // gltfpack defaults + {1, 224, 2, 0.2f}, {1, 255, 2, 0.f}, }; diff --git a/tools/bitmask.py b/tools/bitmask.py old mode 100755 new mode 100644 diff --git a/tools/gltfbasis.py b/tools/gltfbasis.py new file mode 100644 index 00000000..9fc88166 --- /dev/null +++ b/tools/gltfbasis.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 + +import argparse +import concurrent.futures +import matplotlib.pyplot as plt +import os +import os.path +import re +import subprocess + +argp = argparse.ArgumentParser() +argp.add_argument('--basisu', type=str, required=True) +argp.add_argument('--graph', type=str, default="basisu.png") +argp.add_argument('--etcbase', action='store_true') +argp.add_argument('files', type=str, nargs='+') +args = argp.parse_args() + +def compress(path, flags): + temp_path = "/dev/null" if os.name == "posix" else "NUL" + output = subprocess.check_output([args.basisu, "-file", path, "-output_file", temp_path, "-stats", "-ktx2"] + flags) + for line in output.splitlines(): + if m := re.match(r".*source image.*?(\d+)x(\d+)", line.decode()): + pixels = int(m.group(1)) * int(m.group(2)) + elif m := re.match(r".*Compression succeeded.*size (\d+) bytes", line.decode()): + bytes = int(m.group(1)) + elif m := re.match(r"\.basis RGB Avg:.*RMS: (\d+\.\d+) PSNR: (\d+\.\d+)", line.decode()): + rms = float(m.group(1)) + psnr = float(m.group(2)) + + return {'path': path, 'bpp': bytes * 8 / pixels, 'rms': rms, 'psnr': psnr} + +def stats(path): + with concurrent.futures.ThreadPoolExecutor(16) as executor: + futures = [] + for i in range(0, 26): + rdo_l = i / 5 + flags = ["-uastc", "-uastc_level", "1", "-uastc_rdo_l", str(rdo_l), "-uastc_rdo_d", "1024"] + futures.append((executor.submit(compress, path, flags), rdo_l)) + concurrent.futures.wait([f for (f, r) in futures]) + results = [] + bppbase = 0 + for future, rdo_l in futures: + res = future.result() + if rdo_l == 0: + bppbase = res['bpp'] + res['rdo_l'] = rdo_l + res['ratio'] = res['bpp'] / bppbase + results.append(res) + return results + +fields = ['bpp', 'rms', 'psnr', 'ratio'] +fig, axs = plt.subplots(1, len(fields) + 1, layout='constrained') +fig.set_figwidth(5 * (len(fields) + 1)) +lines = [] + +for path in args.files: + print('Processing', path) + results = stats(path) + etcbase = compress(path, ["-q", "192"]) if args.etcbase else None + + for idx, field in enumerate(fields): + line, = axs[idx].plot([r['rdo_l'] for r in results], [r[field] for r in results]) + if etcbase and field in etcbase: + axs[idx].axhline(etcbase[field], color=line.get_color(), linestyle='dotted') + if idx == 0: + lines.append(line) + + axs[len(fields)].scatter([r['ratio'] for r in results], [r['psnr'] for r in results], color=line.get_color()) + +for idx, field in enumerate(fields): + axs[idx].set_title(field) + +axs[len(fields)].set_title('psnr vs ratio') + +fig.legend(lines, [os.path.basename(path) for path in args.files], loc='outside right upper') + +plt.savefig(args.graph)