mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 00:03:50 +00:00
Unlimited creature model uploads during load screen, remove duplicate code
Loading screen now calls processCreatureSpawnQueue(unlimited=true) which removes the 1-upload-per-frame cap and 2ms time budget, allowing all pending creature models to upload to GPU in bulk. Also increases concurrent async background loads from 4 to 16 during load screen. Replaces 40-line inline duplicate of processAsyncCreatureResults with the shared function.
This commit is contained in:
parent
24f2ec75ec
commit
63efac9fa6
2 changed files with 14 additions and 58 deletions
|
|
@ -215,7 +215,7 @@ private:
|
||||||
std::future<PreparedCreatureModel> future;
|
std::future<PreparedCreatureModel> future;
|
||||||
};
|
};
|
||||||
std::vector<AsyncCreatureLoad> asyncCreatureLoads_;
|
std::vector<AsyncCreatureLoad> asyncCreatureLoads_;
|
||||||
void processAsyncCreatureResults();
|
void processAsyncCreatureResults(bool unlimited = false);
|
||||||
static constexpr int MAX_ASYNC_CREATURE_LOADS = 4; // concurrent background loads
|
static constexpr int MAX_ASYNC_CREATURE_LOADS = 4; // concurrent background loads
|
||||||
std::unordered_set<uint64_t> deadCreatureGuids_; // GUIDs that should spawn in corpse/death pose
|
std::unordered_set<uint64_t> deadCreatureGuids_; // GUIDs that should spawn in corpse/death pose
|
||||||
std::unordered_map<uint32_t, uint32_t> displayIdModelCache_; // displayId → modelId (model caching)
|
std::unordered_map<uint32_t, uint32_t> displayIdModelCache_; // displayId → modelId (model caching)
|
||||||
|
|
@ -373,7 +373,7 @@ private:
|
||||||
std::unordered_set<uint64_t> pendingPlayerSpawnGuids_;
|
std::unordered_set<uint64_t> pendingPlayerSpawnGuids_;
|
||||||
void processPlayerSpawnQueue();
|
void processPlayerSpawnQueue();
|
||||||
std::unordered_set<uint64_t> creaturePermanentFailureGuids_;
|
std::unordered_set<uint64_t> creaturePermanentFailureGuids_;
|
||||||
void processCreatureSpawnQueue();
|
void processCreatureSpawnQueue(bool unlimited = false);
|
||||||
|
|
||||||
struct PendingGameObjectSpawn {
|
struct PendingGameObjectSpawn {
|
||||||
uint64_t guid;
|
uint64_t guid;
|
||||||
|
|
|
||||||
|
|
@ -4207,53 +4207,8 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
|
||||||
processPlayerSpawnQueue();
|
processPlayerSpawnQueue();
|
||||||
|
|
||||||
// During load screen warmup: lift per-frame budgets so GPU uploads
|
// During load screen warmup: lift per-frame budgets so GPU uploads
|
||||||
// happen in bulk while the loading screen is still visible.
|
// and spawns happen in bulk while the loading screen is still visible.
|
||||||
// Process ALL async creature model uploads (no 3-per-frame cap).
|
processCreatureSpawnQueue(true); // unlimited: no model upload cap, no time budget
|
||||||
{
|
|
||||||
for (auto it = asyncCreatureLoads_.begin(); it != asyncCreatureLoads_.end(); ) {
|
|
||||||
if (!it->future.valid() ||
|
|
||||||
it->future.wait_for(std::chrono::milliseconds(0)) != std::future_status::ready) {
|
|
||||||
++it;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto result = it->future.get();
|
|
||||||
it = asyncCreatureLoads_.erase(it);
|
|
||||||
if (result.permanent_failure) {
|
|
||||||
nonRenderableCreatureDisplayIds_.insert(result.displayId);
|
|
||||||
creaturePermanentFailureGuids_.insert(result.guid);
|
|
||||||
pendingCreatureSpawnGuids_.erase(result.guid);
|
|
||||||
creatureSpawnRetryCounts_.erase(result.guid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!result.valid || !result.model) {
|
|
||||||
pendingCreatureSpawnGuids_.erase(result.guid);
|
|
||||||
creatureSpawnRetryCounts_.erase(result.guid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto* charRenderer = renderer ? renderer->getCharacterRenderer() : nullptr;
|
|
||||||
if (!charRenderer) { pendingCreatureSpawnGuids_.erase(result.guid); continue; }
|
|
||||||
if (!charRenderer->loadModel(*result.model, result.modelId)) {
|
|
||||||
nonRenderableCreatureDisplayIds_.insert(result.displayId);
|
|
||||||
creaturePermanentFailureGuids_.insert(result.guid);
|
|
||||||
pendingCreatureSpawnGuids_.erase(result.guid);
|
|
||||||
creatureSpawnRetryCounts_.erase(result.guid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
displayIdModelCache_[result.displayId] = result.modelId;
|
|
||||||
pendingCreatureSpawnGuids_.erase(result.guid);
|
|
||||||
creatureSpawnRetryCounts_.erase(result.guid);
|
|
||||||
if (!creatureInstances_.count(result.guid) &&
|
|
||||||
!creaturePermanentFailureGuids_.count(result.guid)) {
|
|
||||||
PendingCreatureSpawn s{};
|
|
||||||
s.guid = result.guid; s.displayId = result.displayId;
|
|
||||||
s.x = result.x; s.y = result.y; s.z = result.z;
|
|
||||||
s.orientation = result.orientation;
|
|
||||||
pendingCreatureSpawns_.push_back(s);
|
|
||||||
pendingCreatureSpawnGuids_.insert(result.guid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processCreatureSpawnQueue();
|
|
||||||
processAsyncNpcCompositeResults();
|
processAsyncNpcCompositeResults();
|
||||||
processDeferredEquipmentQueue();
|
processDeferredEquipmentQueue();
|
||||||
if (auto* cr = renderer ? renderer->getCharacterRenderer() : nullptr) {
|
if (auto* cr = renderer ? renderer->getCharacterRenderer() : nullptr) {
|
||||||
|
|
@ -6804,9 +6759,10 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t
|
||||||
" displayId=", displayId, " at (", x, ", ", y, ", ", z, ")");
|
" displayId=", displayId, " at (", x, ", ", y, ", ", z, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::processAsyncCreatureResults() {
|
void Application::processAsyncCreatureResults(bool unlimited) {
|
||||||
// Check completed async model loads and finalize on main thread (GPU upload + instance creation).
|
// Check completed async model loads and finalize on main thread (GPU upload + instance creation).
|
||||||
// Limit GPU model uploads per frame to avoid spikes, but always drain cheap bookkeeping.
|
// Limit GPU model uploads per frame to avoid spikes, but always drain cheap bookkeeping.
|
||||||
|
// In unlimited mode (load screen), process all pending uploads without cap.
|
||||||
static constexpr int kMaxModelUploadsPerFrame = 1;
|
static constexpr int kMaxModelUploadsPerFrame = 1;
|
||||||
int modelUploads = 0;
|
int modelUploads = 0;
|
||||||
|
|
||||||
|
|
@ -6819,9 +6775,7 @@ void Application::processAsyncCreatureResults() {
|
||||||
|
|
||||||
// Peek: if this result needs a NEW model upload (not cached) and we've hit
|
// Peek: if this result needs a NEW model upload (not cached) and we've hit
|
||||||
// the upload budget, defer to next frame without consuming the future.
|
// the upload budget, defer to next frame without consuming the future.
|
||||||
if (modelUploads >= kMaxModelUploadsPerFrame) {
|
if (!unlimited && modelUploads >= kMaxModelUploadsPerFrame) {
|
||||||
// Check if this displayId already has a cached model (cheap spawn, no GPU upload).
|
|
||||||
// We can't peek the displayId without getting the future, so just break.
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6967,13 +6921,14 @@ void Application::processAsyncNpcCompositeResults() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::processCreatureSpawnQueue() {
|
void Application::processCreatureSpawnQueue(bool unlimited) {
|
||||||
auto startTime = std::chrono::steady_clock::now();
|
auto startTime = std::chrono::steady_clock::now();
|
||||||
// Budget: max 2ms per frame for creature spawning to prevent stutter.
|
// Budget: max 2ms per frame for creature spawning to prevent stutter.
|
||||||
|
// In unlimited mode (load screen), process everything without budget cap.
|
||||||
static constexpr float kSpawnBudgetMs = 2.0f;
|
static constexpr float kSpawnBudgetMs = 2.0f;
|
||||||
|
|
||||||
// First, finalize any async model loads that completed on background threads.
|
// First, finalize any async model loads that completed on background threads.
|
||||||
processAsyncCreatureResults();
|
processAsyncCreatureResults(unlimited);
|
||||||
{
|
{
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
float asyncMs = std::chrono::duration<float, std::milli>(now - startTime).count();
|
float asyncMs = std::chrono::duration<float, std::milli>(now - startTime).count();
|
||||||
|
|
@ -6992,11 +6947,11 @@ void Application::processCreatureSpawnQueue() {
|
||||||
int asyncLaunched = 0;
|
int asyncLaunched = 0;
|
||||||
size_t rotationsLeft = pendingCreatureSpawns_.size();
|
size_t rotationsLeft = pendingCreatureSpawns_.size();
|
||||||
while (!pendingCreatureSpawns_.empty() &&
|
while (!pendingCreatureSpawns_.empty() &&
|
||||||
processed < MAX_SPAWNS_PER_FRAME &&
|
(unlimited || processed < MAX_SPAWNS_PER_FRAME) &&
|
||||||
rotationsLeft > 0) {
|
rotationsLeft > 0) {
|
||||||
// Check time budget every iteration (including first — async results may
|
// Check time budget every iteration (including first — async results may
|
||||||
// have already consumed the budget via GPU model uploads).
|
// have already consumed the budget via GPU model uploads).
|
||||||
{
|
if (!unlimited) {
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
float elapsedMs = std::chrono::duration<float, std::milli>(now - startTime).count();
|
float elapsedMs = std::chrono::duration<float, std::milli>(now - startTime).count();
|
||||||
if (elapsedMs >= kSpawnBudgetMs) break;
|
if (elapsedMs >= kSpawnBudgetMs) break;
|
||||||
|
|
@ -7017,7 +6972,8 @@ void Application::processCreatureSpawnQueue() {
|
||||||
|
|
||||||
// For new models: launch async load on background thread instead of blocking.
|
// For new models: launch async load on background thread instead of blocking.
|
||||||
if (needsNewModel) {
|
if (needsNewModel) {
|
||||||
if (static_cast<int>(asyncCreatureLoads_.size()) + asyncLaunched >= MAX_ASYNC_CREATURE_LOADS) {
|
const int maxAsync = unlimited ? (MAX_ASYNC_CREATURE_LOADS * 4) : MAX_ASYNC_CREATURE_LOADS;
|
||||||
|
if (static_cast<int>(asyncCreatureLoads_.size()) + asyncLaunched >= maxAsync) {
|
||||||
// Too many in-flight — defer to next frame
|
// Too many in-flight — defer to next frame
|
||||||
pendingCreatureSpawns_.push_back(s);
|
pendingCreatureSpawns_.push_back(s);
|
||||||
rotationsLeft--;
|
rotationsLeft--;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue