feat(dashboard): pick processing options for Make again; compare them on the UGC page

The UGC page's Make again buttons (a model's, the failed ones, everything) use
the ray tracer, hidden-face method and denoising picked next to them (the
settings' when left alone). The List view's new Options column shows what made
each model and what its next make will use; "Processing options compared"
shows every combination the made models used with its makes, models and the
average time, CPU time, hidden faces', occlusion's and icon's time and share of
triangles removed per make.

Check: the three selects next to Make again (models only); make a model again
with embree fast and see its Options column and the comparison table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 11:28:52 -05:00
parent ef3c74f715
commit 1700b7e2e3
2 changed files with 57 additions and 4 deletions

View File

@@ -125,10 +125,14 @@
if (!i.processMs) return '';
return 'took ' + duration(i.processMs) + (i.processCpuMs ? ', CPU ' + duration(i.processCpuMs) : '') + (i.processMemoryKb ? ', ~' + megabytes(i.processMemoryKb) + ' RAM (est.)' : '');
}
// When it was made and what that cost (the cost is only known for makes since it was recorded)
// When it was made and what that cost (the cost is only known for makes since it was recorded), and with what
function madeText(i) {
if (!i.processedAt) return '';
return fmt.unix(i.processedAt) + (costText(i) ? ' \u00b7 ' + costText(i) : '');
return fmt.unix(i.processedAt) + (costText(i) ? ' \u00b7 ' + costText(i) : '') + (i.madeOptions ? ' \u00b7 with ' + i.madeOptions : '');
}
// The processing options picked for Make again (ray tracer, hidden faces, denoise), as /api/ugc/reprocess takes them
function remakeOptions() {
return ['optRays', 'optHsr', 'optDenoise'].map(function (x) { return $(x).value; }).filter(Boolean).join(' ');
}
// A model's file as the player named it: its name with the upload's extension (".lxfml"), else the upload's name
function fileName(i) {
@@ -178,6 +182,10 @@
return badge(i.state) + (i.attempts ? ' <span class="small text-body-secondary">' + esc(i.attempts) + ' attempt' + (i.attempts === 1 ? '' : 's') + '</span>' : '') + waitBadge(i);
})
].concat(madeColumns(), [
column('Options', function (i) {
return '<span class="small">' + esc(i.madeOptions || '') + '</span>' +
(i.processOptions ? '<div class="small text-body-secondary" title="Picked for its next make">next: ' + esc(i.processOptions) + '</div>' : '');
}),
column('Saved', function (i) {
if (!i.trianglesBefore) return '';
var removed = i.trianglesBefore - i.triangles, share = removed / i.trianglesBefore * 100;
@@ -256,6 +264,7 @@
b.textContent = (b.dataset.kind === 'modular' ? 'Cars and rockets' : 'Models') + (count === null || count === undefined ? '' : ' (' + Number(count).toLocaleString() + ')');
});
$('manageButtons').classList.toggle('d-none', !canManage);
$('remakeOptions').classList.toggle('d-none', list.kind !== 'model');
$('cacheCard').classList.toggle('d-none', !canManage);
$('counts').innerHTML = countCard('Models', d.counts.model) + countCard('Cars and rockets (builds)', d.counts.modular) +
(d.totals ? totalsCard('Models', d.totals.model) + totalsCard('Cars and rockets', d.totals.modular) : '');
@@ -366,6 +375,7 @@
function remake(body, question) {
if (question && !confirm(question)) return;
body.kind = list.kind;
if (list.kind === 'model' && remakeOptions()) body.options = remakeOptions();
api.post('/api/ugc/reprocess', body).then(function (d) {
if (!d.success) { toast(d.error || 'Failed', 'danger'); return; }
toast(d.message, 'success');
@@ -715,6 +725,31 @@
}
loadPresets();
// ---- processing options: Make again's choices and the comparison of what made the models ----
var OPTION_LABELS = { builtin: 'Built in', embree: 'Embree (CPU)', hiprt: 'HIPRT (GPU)', toolbox: 'LU Toolbox (paths)', fast: 'Fast (renders)', off: 'Off', oidn: 'Open Image Denoise' };
function loadOptions() {
return api.get('/api/ugc/options').then(function (d) {
if (!d.success) throw new Error(d.error || 'Failed');
[['optRays', 'rays', 'Ray tracer'], ['optHsr', 'hsr', 'Hidden faces'], ['optDenoise', 'denoise', 'Denoise']].forEach(function (x) {
var select = $(x[0]), chosen = select.value;
select.innerHTML = '<option value="">' + esc(x[2]) + ': settings (' + esc(OPTION_LABELS[d.defaults[x[1]]] || d.defaults[x[1]]) + ')</option>' +
d.choices[x[1]].map(function (name) { return '<option value="' + esc(name) + '">' + esc(x[2]) + ': ' + esc(OPTION_LABELS[name] || name) + '</option>'; }).join('');
select.value = chosen;
});
var ms = function (v) { return v ? esc(duration(v)) : ''; };
$('optionsTable').querySelector('tbody').innerHTML = d.combinations.length ? d.combinations.map(function (c) {
return '<tr><td><code>' + esc(c.options) + '</code></td><td>' + esc(c.runs.toLocaleString()) + '</td><td>' + esc(c.models.toLocaleString()) + '</td><td>' + ms(c.ms) +
'</td><td>' + ms(c.cpuMs) + '</td><td>' + ms(c.hsrMs) + '</td><td>' + ms(c.aoMs) + '</td><td>' + ms(c.iconMs) + '</td><td>' + esc(c.bricks) + '</td><td title="' +
esc(c.trianglesBefore.toLocaleString()) + ' before, ' + esc(c.triangles.toLocaleString()) + ' after, on average">' + esc((c.removed * 100).toFixed(1)) + '%</td></tr>';
}).join('') : '<tr><td colspan="10" class="text-body-secondary">No model made since the options were recorded.</td></tr>';
}).catch(function (e) {
$('optionsTable').querySelector('tbody').innerHTML = '<tr><td colspan="10" class="text-danger">Could not load the options: ' + esc(e.message) + '</td></tr>';
});
}
loadOptions();
$('optionsCard').addEventListener('toggle', function () { if (this.open) loadOptions(); });
if (window.Live) Live.on('ugc', load);
var linked = readUrl();

View File

@@ -32,6 +32,19 @@
</div>
</details>
<details class="card mb-3" id="optionsCard">
<summary class="card-header py-2 small fw-semibold">Processing options compared</summary>
<div class="card-body py-2 small">
<p class="text-body-secondary mb-2">The ways of making models to compare: the ray tracer (built in, Embree on the CPU, HIPRT on the GPU), how
hidden faces are found (LU Toolbox's paths, or fast renders from around the model) and denoising of icons. The settings pick the
defaults; <b>Make again</b> can use others for a make (the options next to it, or <code>/reprocessproperty embree fast</code> in game).
Every make is counted under what made it: the averages per make below, each model's in the List view's Options column.</p>
<div class="table-responsive"><table class="table table-sm align-middle mb-0" id="optionsTable">
<thead><tr><th>Options</th><th>Makes</th><th>Models</th><th>Took</th><th>CPU</th><th>Hidden faces</th><th>Occlusion</th><th>Icon</th><th>Bricks</th><th>Triangles removed</th></tr></thead>
<tbody><tr><td colspan="10" class="text-body-secondary">Loading…</td></tr></tbody></table></div>
</div>
</details>
<details class="card mb-3 d-none" id="cacheCard">
<summary class="card-header py-2 small fw-semibold">Stored files: delete and purge</summary>
<div class="card-body py-2 small">
@@ -67,7 +80,12 @@
<button type="button" class="btn btn-outline-secondary active" data-view="gallery">Icons</button>
<button type="button" class="btn btn-outline-secondary" data-view="list">List</button>
</div>
<div class="ms-auto d-none" id="manageButtons">
<div class="ms-auto d-none d-flex flex-wrap gap-1 align-items-center" id="manageButtons">
<span class="d-flex gap-1" id="remakeOptions" title="How Make again makes models (the settings' way unless picked here)">
<select class="form-select form-select-sm w-auto" id="optRays" aria-label="Ray tracer for Make again"><option value="">Ray tracer: settings</option></select>
<select class="form-select form-select-sm w-auto" id="optHsr" aria-label="Hidden faces for Make again"><option value="">Hidden faces: settings</option></select>
<select class="form-select form-select-sm w-auto" id="optDenoise" aria-label="Denoise for Make again"><option value="">Denoise: settings</option></select>
</span>
<button type="button" class="btn btn-sm btn-outline-warning" id="retryFailed">Try the failed ones again</button>
<button type="button" class="btn btn-sm btn-outline-danger" id="remakeAll">Make everything again</button>
</div>
@@ -76,7 +94,7 @@
<div id="gallery" class="ugc-gallery mb-2"></div>
<div class="card d-none" id="listCard"><div class="card-body p-2">
<div id="modelTableBox"><table class="table table-sm align-middle mb-0 w-100 table-stack" id="ugcModelTable">
<thead><tr><th>Icon</th><th>ID</th><th>Owner</th><th>State</th><th>Made</th><th>Took</th><th>CPU</th><th>RAM (est.)</th><th>Saved</th><th>Size</th><th>File</th><th>Where</th><th></th></tr></thead></table></div>
<thead><tr><th>Icon</th><th>ID</th><th>Owner</th><th>State</th><th>Made</th><th>Took</th><th>CPU</th><th>RAM (est.)</th><th>Options</th><th>Saved</th><th>Size</th><th>File</th><th>Where</th><th></th></tr></thead></table></div>
<div id="assemblyTableBox" class="d-none"><table class="table table-sm align-middle mb-0 w-100 table-stack" id="ugcAssemblyTable">
<thead><tr><th>Icon</th><th>ID</th><th>Owner</th><th>State</th><th>Made</th><th>Took</th><th>CPU</th><th>RAM (est.)</th><th>Size</th><th>File</th><th>Where</th><th></th></tr></thead></table></div>
</div></div>