mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
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:
@@ -297,6 +297,9 @@ void UgcProcessor::Worker() {
|
||||
const double iconMs = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - iconStart).count();
|
||||
if (const auto updated = stats ? UgcJobs::WithIconTime(*stats, iconMs, done.iconChangeMs) : std::nullopt) done.outcome.files["stats.json"] = *updated;
|
||||
} else {
|
||||
// The processing options staff picked for this make, over the settings (unknown ones: the settings')
|
||||
UgcProcessOptions::Choice choice;
|
||||
if (job.kind == Kind::MODEL && UgcProcessOptions::Parse(job.options, choice)) UgcJobs::ApplyOptions(settings, choice);
|
||||
done.outcome = job.kind == Kind::MODEL
|
||||
? UgcJobs::ProcessModel(job.blob, m_Library, settings, static_cast<uint64_t>(job.id), job.iconValues)
|
||||
: UgcJobs::ProcessModular(job.modular, m_Library.GetResPath(), settings);
|
||||
@@ -393,6 +396,7 @@ void UgcProcessor::Poll() {
|
||||
if (m_InFlight.contains({ Kind::MODEL, model.id })) continue;
|
||||
Job job{ Kind::MODEL, model.id, model.attempts, UgcJobs::LxfmlFromBlob(model.lxfml) };
|
||||
job.priority = model.priority;
|
||||
job.options = std::move(model.options);
|
||||
if (job.blob.empty()) job.blob = std::move(model.lxfml); // the worker reports it can't be read
|
||||
job.parts = UgcJobs::CountParts(job.blob);
|
||||
job.iconValues = IconValues(UgcIconParams::ModelKind(), UgcIconParams::ModelTarget(model.id));
|
||||
@@ -469,7 +473,20 @@ void UgcProcessor::Record(const Done& done) {
|
||||
if (done.outcome.ok) Database::Get()->SetUgcModelProcessStats(done.id, cost);
|
||||
// What it counted (stats.json), for sorting on the dashboard
|
||||
const auto stats = done.outcome.ok && !done.outcome.stats.empty() ? nlohmann::json::parse(done.outcome.stats, nullptr, false) : nlohmann::json();
|
||||
if (const auto counts = CountsOf(stats)) Database::Get()->SetUgcModelStats(done.id, counts->bricks, counts->triangles, counts->trianglesBefore);
|
||||
const auto counts = CountsOf(stats);
|
||||
if (counts) Database::Get()->SetUgcModelStats(done.id, counts->bricks, counts->triangles, counts->trianglesBefore);
|
||||
// The make with its options and times, for comparing the options on the dashboard
|
||||
if (done.outcome.ok && !done.outcome.options.empty()) {
|
||||
const auto& ms = stats.is_object() ? stats.value("ms", nlohmann::json::object()) : nlohmann::json::object();
|
||||
const auto part = [&ms](const char* name) { return ms.is_object() ? ms.value(name, 0u) : 0u; };
|
||||
IUgc::ProcessRun run{ done.id, done.outcome.options, cost.milliseconds, cost.cpuMilliseconds, part("hiddenSurfaces"), part("ambientOcclusion"), part("icon") };
|
||||
if (counts) {
|
||||
run.bricks = counts->bricks;
|
||||
run.trianglesBefore = counts->trianglesBefore;
|
||||
run.triangles = counts->triangles;
|
||||
}
|
||||
Database::Get()->RecordUgcModelRun(run);
|
||||
}
|
||||
} else {
|
||||
Database::Get()->SetModularBuildProcessed(done.id, state, attempts, error);
|
||||
if (done.outcome.ok) Database::Get()->SetModularBuildProcessStats(done.id, cost);
|
||||
@@ -656,7 +673,7 @@ UgcProcessor::Availability UgcProcessor::Request(Kind kind, LWOOBJID id) {
|
||||
if (info && info->state != IUgc::eProcessState::FAILED && info->state != IUgc::eProcessState::EMPTY) {
|
||||
// Made before but the files are gone (deleted to save space): make them again, first
|
||||
if (info->state == IUgc::eProcessState::DONE) {
|
||||
if (kind == Kind::MODEL) Database::Get()->ResetUgcModelProcessing(id, false);
|
||||
if (kind == Kind::MODEL) Database::Get()->ResetUgcModelProcessing(id, false, "");
|
||||
else Database::Get()->ResetModularBuildProcessing(id, false);
|
||||
}
|
||||
// Someone wants it now: no need to wait out the quiet period after its save
|
||||
@@ -1007,7 +1024,7 @@ void UgcProcessor::CollectFileTask() {
|
||||
|
||||
constexpr size_t ROWS_PER_TICK = 250;
|
||||
if (m_Purge.after == eAfterDelete::NOW && m_Purge.all) {
|
||||
if (kind == Kind::MODEL) Database::Get()->ResetUgcModelProcessing(std::nullopt, false);
|
||||
if (kind == Kind::MODEL) Database::Get()->ResetUgcModelProcessing(std::nullopt, false, "");
|
||||
else Database::Get()->ResetModularBuildProcessing(std::nullopt, false);
|
||||
m_PurgeRowsDone = m_PurgeRows.size();
|
||||
} else if (m_Purge.after != eAfterDelete::ON_DEMAND) {
|
||||
@@ -1017,7 +1034,7 @@ void UgcProcessor::CollectFileTask() {
|
||||
for (; m_PurgeRowsDone < end; m_PurgeRowsDone++) {
|
||||
const auto row = m_PurgeRows[m_PurgeRowsDone];
|
||||
if (m_Purge.after == eAfterDelete::NOW) {
|
||||
if (kind == Kind::MODEL) Database::Get()->ResetUgcModelProcessing(row, false);
|
||||
if (kind == Kind::MODEL) Database::Get()->ResetUgcModelProcessing(row, false, "");
|
||||
else Database::Get()->ResetModularBuildProcessing(row, false);
|
||||
} else {
|
||||
if (kind == Kind::MODEL) Database::Get()->SetUgcModelProcessed(row, IUgc::eProcessState::FAILED, m_Config.maxAttempts, "Deleted from the dashboard", false);
|
||||
|
||||
@@ -178,6 +178,7 @@ private:
|
||||
bool assembly{}; // with `preview`: answered with the assembled .nif instead of an icon
|
||||
UgcIconParams::Values iconValues; // models: the preset and override (UgcIconParams)
|
||||
bool priority{}; // models staff asked to be made again: queued at the front
|
||||
std::string options; // models: the processing options staff picked (UgcProcessOptions), over the settings
|
||||
};
|
||||
|
||||
// A written file's MD5 and size as the client has it after inflating it (from its .checksum)
|
||||
|
||||
Reference in New Issue
Block a user