feat(ugc): processing options per make, recorded per model with their times

Staff can make models again with other processing options than the UGC
settings' (ray backend, hidden-face method, denoising) and every make records
what made it, for comparing the options.

- migration mysql 99 / sqlite 82 (ugc_process_options): ugc.process_options
  (picked for the next make, cleared once made), ugc.made_options (what made
  the current files) and ugc_process_runs (every successful make: options,
  wall and CPU time, hidden faces', occlusion's and icon's time, bricks,
  triangles before and after)
- IUgc: ResetUgcModelProcessing and ResetPropertyUgcModelProcessing take the
  options; PendingModel carries them; RecordUgcModelRun, GetUgcRunSummaries;
  list entries have madeOptions and processOptions
- the UGC server applies a job's options over its settings and records the run
- /api/ugc/reprocess takes options ("embree fast oidn"); /api/ugc/options
  lists the choices, the settings' defaults and averages per combination
- /reprocessproperty [builtin|embree|hiprt] [toolbox|fast] [off|oidn], any
  order, all optional

Check: run the migration on MySQL and SQLite; /reprocessproperty embree fast
on a property, then the models' made_options and ugc_process_runs rows;
/api/ugc/options.

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 06be40471b
commit ef3c74f715
22 changed files with 290 additions and 58 deletions

View File

@@ -774,7 +774,7 @@ TEST_F(ParitySeeded, UgcModel) {
return out;
});
Both("ResetUgcModelProcessing", [&](GameDatabase& db) {
const auto changed = db.ResetUgcModelProcessing(std::nullopt, true);
const auto changed = db.ResetUgcModelProcessing(std::nullopt, true, "");
db.SetUgcModelProcessed(1152921510000500001LL, IUgc::eProcessState::DONE, 1, "", true);
return json{ changed, infoJson(db.GetUgcProcessInfo(1152921510000500001LL)), infoJson(db.GetUgcProcessInfo(1)) };
});
@@ -793,17 +793,38 @@ TEST_F(ParitySeeded, UgcModel) {
IPropertyContents::Model model;
model.id = 1152921510000400009LL; model.lot = 14; model.ugcId = 1152921510000500001LL;
db.InsertNewPropertyModel(PROP1, model, "Objects_14_name");
const auto changed = db.ResetPropertyUgcModelProcessing(PROP1);
const auto changed = db.ResetPropertyUgcModelProcessing(PROP1, "");
// Reset as priority: first in line, and the only one when asking for priority models; made, it's cleared
json priority = json::array();
for (const auto& model : db.GetUgcModelsToProcess(10, true)) priority.push_back({ model.id, model.priority });
const auto first = db.GetUgcModelsToProcess(10);
db.SetUgcModelProcessed(1152921510000500001LL, IUgc::eProcessState::DONE, 1, "", true);
db.ResetUgcModelProcessing(1152921510000500001LL, false);
db.ResetUgcModelProcessing(1152921510000500001LL, false, "");
const auto afterMade = db.GetUgcModelsToProcess(10, true).size();
return json{ changed, infoJson(db.GetUgcProcessInfo(1152921510000500001LL)), db.ResetPropertyUgcModelProcessing(1), priority,
return json{ changed, infoJson(db.GetUgcProcessInfo(1152921510000500001LL)), db.ResetPropertyUgcModelProcessing(1, ""), priority,
first.empty() ? json() : json{ first.front().id, first.front().priority }, afterMade };
});
Both("Processing options and runs", [&](GameDatabase& db) {
// Options picked for the next make reach the UGC server and are cleared once it's made; a failed attempt keeps them
json out = json::array();
out.push_back(db.ResetUgcModelProcessing(1152921510000500001LL, false, "embree fast"));
for (const auto& model : db.GetUgcModelsToProcess(10)) out.push_back({ model.id, model.options });
db.SetUgcModelProcessed(1152921510000500001LL, IUgc::eProcessState::PENDING, 1, "crashed", false);
for (const auto& model : db.GetUgcModelsToProcess(10)) out.push_back({ model.id, model.options });
db.SetUgcModelProcessed(1152921510000500001LL, IUgc::eProcessState::DONE, 2, "", true);
out.push_back(db.ResetPropertyUgcModelProcessing(PROP1, "hiprt oidn"));
for (const auto& model : db.GetUgcModelsToProcess(10)) out.push_back({ model.id, model.options, model.priority });
db.SetUgcModelProcessed(1152921510000500001LL, IUgc::eProcessState::DONE, 1, "", true);
// Runs: the made options on the model, and the sums per options
db.RecordUgcModelRun({ 1152921510000500001LL, "embree fast off", 1000, 900, 100, 200, 50, 12, 500, 300 });
db.RecordUgcModelRun({ 1152921510000500001LL, "embree fast off", 3000, 2900, 300, 400, 70, 12, 500, 280 });
db.RecordUgcModelRun({ 1152921510000500001LL, "builtin toolbox off", 9000, 8900, 7000, 900, 60, 12, 500, 350 });
for (const auto& entry : db.GetUgcEntries({ 1152921510000500001LL })) out.push_back({ entry.madeOptions, entry.processOptions });
for (const auto& s : db.GetUgcRunSummaries()) {
out.push_back({ s.options, s.runs, s.models, s.milliseconds, s.cpuMilliseconds, s.hsrMilliseconds, s.aoMilliseconds, s.iconMilliseconds, s.bricks, s.trianglesBefore, s.triangles });
}
return out;
});
Both("Modular build processing", [&](GameDatabase& db) {
db.InsertUgcBuild("1:4713+1:4714+1:4715", 1152921510000500002LL, CHAR_ALICE);
json out = json::array();

View File

@@ -105,7 +105,7 @@ namespace {
"CREATE TABLE charinfo (id BIGINT PRIMARY KEY, account_id INTEGER, name TEXT);"
"CREATE TABLE properties (id BIGINT PRIMARY KEY, owner_id BIGINT, name TEXT, zone_id INTEGER);"
"CREATE TABLE ugc (id INTEGER PRIMARY KEY, account_id INTEGER, character_id BIGINT, is_optimized INTEGER, filename TEXT, process_error TEXT DEFAULT '', "
"process_attempts INTEGER DEFAULT 0, processed_at BIGINT DEFAULT 0, process_after BIGINT DEFAULT 0, bake_ao INTEGER DEFAULT 0, brick_count INTEGER DEFAULT 0, triangle_count INTEGER DEFAULT 0, process_ms INTEGER DEFAULT 0, process_cpu_ms INTEGER DEFAULT 0, process_memory_kb INTEGER DEFAULT 0, triangle_count_before INTEGER DEFAULT 0);"
"process_attempts INTEGER DEFAULT 0, processed_at BIGINT DEFAULT 0, process_after BIGINT DEFAULT 0, bake_ao INTEGER DEFAULT 0, brick_count INTEGER DEFAULT 0, triangle_count INTEGER DEFAULT 0, process_ms INTEGER DEFAULT 0, process_cpu_ms INTEGER DEFAULT 0, process_memory_kb INTEGER DEFAULT 0, triangle_count_before INTEGER DEFAULT 0, made_options TEXT DEFAULT '', process_options TEXT DEFAULT '');"
"CREATE TABLE ugc_modular_build (ugc_id BIGINT PRIMARY KEY, character_id BIGINT, ldf_config TEXT, is_optimized INTEGER DEFAULT 0, process_error TEXT DEFAULT '', "
"process_attempts INTEGER DEFAULT 0, processed_at BIGINT DEFAULT 0, process_ms INTEGER DEFAULT 0, process_cpu_ms INTEGER DEFAULT 0, process_memory_kb INTEGER DEFAULT 0);"
"CREATE TABLE properties_contents (id BIGINT PRIMARY KEY, property_id BIGINT, ugc_id BIGINT, lot INTEGER, model_name TEXT DEFAULT '', model_description TEXT DEFAULT '');"