Fix Stockades crash: suppress area triggers on initial login, handle VK_ERROR_DEVICE_LOST

Root cause: LOGIN_VERIFY_WORLD path did not set areaTriggerCheckTimer_ or
areaTriggerSuppressFirst_, so the Stockades exit portal (AT 503) fired
immediately on login, teleporting the player back to Stormwind and crashing
the GPU during the unexpected map transition.

Fixes:
- Set 5s area trigger cooldown + suppress-first in handleLoginVerifyWorld
  (same as SMSG_NEW_WORLD handler already did for teleports)
- Add deviceLost_ flag to VkContext so beginFrame returns immediately once
  VK_ERROR_DEVICE_LOST is detected, preventing infinite retry loops
- Track device lost from both fence wait and queue submit paths
This commit is contained in:
Kelsi 2026-03-02 08:19:14 -08:00
parent 3c55b09a3f
commit f1caf8c03e
4 changed files with 18 additions and 0 deletions

View file

@ -73,6 +73,8 @@ public:
bool isSwapchainDirty() const { return swapchainDirty; }
void markSwapchainDirty() { swapchainDirty = true; }
bool isDeviceLost() const { return deviceLost_; }
// MSAA
VkSampleCountFlagBits getMsaaSamples() const { return msaaSamples_; }
void setMsaaSamples(VkSampleCountFlagBits samples);
@ -131,6 +133,7 @@ private:
std::vector<VkImageView> swapchainImageViews;
std::vector<VkFramebuffer> swapchainFramebuffers;
bool swapchainDirty = false;
bool deviceLost_ = false;
// Per-frame resources
FrameData frames[MAX_FRAMES_IN_FLIGHT];

View file

@ -3456,6 +3456,12 @@ void GameHandler::handleLoginVerifyWorld(network::Packet& packet) {
mountCallback_(0);
}
// Suppress area triggers on initial login — prevents exit portals from
// immediately firing when spawning inside a dungeon/instance.
activeAreaTriggers_.clear();
areaTriggerCheckTimer_ = -5.0f;
areaTriggerSuppressFirst_ = true;
// Send CMSG_SET_ACTIVE_MOVER (required by some servers)
if (playerGuid != 0 && socket) {
auto activeMoverPacket = SetActiveMoverPacket::build(playerGuid);

View file

@ -946,6 +946,7 @@ void Renderer::applyMsaaChange() {
void Renderer::beginFrame() {
if (!vkCtx) return;
if (vkCtx->isDeviceLost()) return;
// Apply deferred MSAA change between frames (before any rendering state is used)
if (msaaChangePending_) {

View file

@ -1297,6 +1297,8 @@ bool VkContext::recreateSwapchain(int width, int height) {
}
VkCommandBuffer VkContext::beginFrame(uint32_t& imageIndex) {
if (deviceLost_) return VK_NULL_HANDLE;
auto& frame = frames[currentFrame];
// Wait for this frame's fence (with timeout to detect GPU hangs)
@ -1309,6 +1311,9 @@ VkCommandBuffer VkContext::beginFrame(uint32_t& imageIndex) {
}
if (fenceResult != VK_SUCCESS) {
LOG_ERROR("beginFrame[", beginFrameCounter, "] fence wait failed: ", (int)fenceResult);
if (fenceResult == VK_ERROR_DEVICE_LOST) {
deviceLost_ = true;
}
return VK_NULL_HANDLE;
}
@ -1363,6 +1368,9 @@ void VkContext::endFrame(VkCommandBuffer cmd, uint32_t imageIndex) {
VkResult submitResult = vkQueueSubmit(graphicsQueue, 1, &submitInfo, frame.inFlightFence);
if (submitResult != VK_SUCCESS) {
LOG_ERROR("endFrame[", endFrameCounter, "] vkQueueSubmit FAILED: ", (int)submitResult);
if (submitResult == VK_ERROR_DEVICE_LOST) {
deviceLost_ = true;
}
}
VkPresentInfoKHR presentInfo{};