mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-14 00:23:50 +00:00
fix(vulkan): MSAA crash on AMD RADV due to vkCreateRenderPass2 null dispatch
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Instance was created with Vulkan 1.1 but depthResolveSupported_ was gated on the physical device's API version (1.2+ on RADV). This caused vkCreateRenderPass2 (core 1.2) to dispatch through a null function pointer when MSAA was enabled. Now requests 1.2 instance with 1.1 minimum fallback and gates depth resolve on the actual instance API version. Also removes all diagnostic crash-phase instrumentation from the previous investigation.
This commit is contained in:
parent
9c4e61a227
commit
17c16150d6
6 changed files with 22 additions and 89 deletions
|
|
@ -246,6 +246,9 @@ private:
|
|||
bool createDepthResolveImage();
|
||||
void destroyDepthResolveImage();
|
||||
|
||||
// Actual Vulkan API version the instance was created with (gates core 1.2 calls)
|
||||
uint32_t instanceApiVersion_ = VK_API_VERSION_1_1;
|
||||
|
||||
// MSAA depth resolve support (for sampling/copying resolved depth)
|
||||
bool depthResolveSupported_ = false;
|
||||
VkResolveModeFlagBits depthResolveMode_ = VK_RESOLVE_MODE_NONE;
|
||||
|
|
|
|||
|
|
@ -2133,22 +2133,15 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
// Render-phase marker from game_screen.cpp — updated here for 3D/submit phases
|
||||
} } // close wowee::core temporarily
|
||||
extern volatile const char* g_crashRenderPhase;
|
||||
namespace wowee { namespace core {
|
||||
|
||||
void Application::render() {
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_crashRenderPhase = "beginFrame";
|
||||
renderer->beginFrame();
|
||||
|
||||
// Only render 3D world when in-game
|
||||
if (state == AppState::IN_GAME) {
|
||||
g_crashRenderPhase = "renderWorld";
|
||||
if (world) {
|
||||
renderer->renderWorld(world.get(), gameHandler.get());
|
||||
} else {
|
||||
|
|
@ -2158,19 +2151,15 @@ void Application::render() {
|
|||
|
||||
// Render performance HUD (within ImGui frame, before UI ends the frame)
|
||||
if (renderer) {
|
||||
g_crashRenderPhase = "renderHUD";
|
||||
renderer->renderHUD();
|
||||
}
|
||||
|
||||
// Render UI on top (ends ImGui frame with ImGui::Render())
|
||||
if (uiManager) {
|
||||
g_crashRenderPhase = "uiRender";
|
||||
uiManager->render(state, authHandler.get(), gameHandler.get());
|
||||
}
|
||||
|
||||
g_crashRenderPhase = "endFrame";
|
||||
renderer->endFrame();
|
||||
g_crashRenderPhase = "idle";
|
||||
}
|
||||
|
||||
void Application::setupUICallbacks() {
|
||||
|
|
|
|||
13
src/main.cpp
13
src/main.cpp
|
|
@ -27,10 +27,6 @@ static void releaseMouseGrab() {
|
|||
static void releaseMouseGrab() {}
|
||||
#endif
|
||||
|
||||
// Render-phase marker set by GameScreen::render() — lets crash handler
|
||||
// identify which render call was active when backtrace is incomplete.
|
||||
extern volatile const char* g_crashRenderPhase;
|
||||
|
||||
#ifdef __linux__
|
||||
static void crashHandlerSigaction(int sig, siginfo_t* info, void* /*ucontext*/) {
|
||||
releaseMouseGrab();
|
||||
|
|
@ -39,15 +35,14 @@ static void crashHandlerSigaction(int sig, siginfo_t* info, void* /*ucontext*/)
|
|||
const char* sigName = (sig == SIGSEGV) ? "SIGSEGV" :
|
||||
(sig == SIGABRT) ? "SIGABRT" :
|
||||
(sig == SIGFPE) ? "SIGFPE" : "UNKNOWN";
|
||||
const char* phase = (const char*)g_crashRenderPhase;
|
||||
void* faultAddr = info ? info->si_addr : nullptr;
|
||||
fprintf(stderr, "\n=== CRASH: signal %s (%d) renderPhase=%s faultAddr=%p ===\n",
|
||||
sigName, sig, phase ? phase : "?", faultAddr);
|
||||
fprintf(stderr, "\n=== CRASH: signal %s (%d) faultAddr=%p ===\n",
|
||||
sigName, sig, faultAddr);
|
||||
backtrace_symbols_fd(frames, n, STDERR_FILENO);
|
||||
FILE* f = fopen("/tmp/wowee_debug.log", "a");
|
||||
if (f) {
|
||||
fprintf(f, "\n=== CRASH: signal %s (%d) renderPhase=%s faultAddr=%p ===\n",
|
||||
sigName, sig, phase ? phase : "?", faultAddr);
|
||||
fprintf(f, "\n=== CRASH: signal %s (%d) faultAddr=%p ===\n",
|
||||
sigName, sig, faultAddr);
|
||||
fflush(f);
|
||||
backtrace_symbols_fd(frames, n, fileno(f));
|
||||
fclose(f);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
extern volatile const char* g_crashRenderPhase;
|
||||
|
||||
#include "rendering/renderer.hpp"
|
||||
#include "rendering/camera.hpp"
|
||||
#include "rendering/camera_controller.hpp"
|
||||
|
|
@ -734,13 +732,10 @@ void Renderer::applyMsaaChange() {
|
|||
VkSampleCountFlagBits current = vkCtx->getMsaaSamples();
|
||||
if (samples == current) return;
|
||||
|
||||
LOG_WARNING("MSAA change: ", static_cast<int>(current), "x → ", static_cast<int>(samples), "x");
|
||||
|
||||
// Single GPU wait — all subsequent operations are CPU-side object creation
|
||||
vkDeviceWaitIdle(vkCtx->getDevice());
|
||||
|
||||
// Set new MSAA and recreate swapchain (render pass, depth, MSAA image, framebuffers)
|
||||
LOG_WARNING("MSAA: recreating swapchain");
|
||||
vkCtx->setMsaaSamples(samples);
|
||||
if (!vkCtx->recreateSwapchain(window->getWidth(), window->getHeight())) {
|
||||
LOG_ERROR("MSAA change failed — reverting to 1x");
|
||||
|
|
@ -749,7 +744,6 @@ void Renderer::applyMsaaChange() {
|
|||
}
|
||||
|
||||
// Recreate all sub-renderer pipelines (they embed sample count from render pass)
|
||||
LOG_WARNING("MSAA: recreating terrain/water/wmo/m2/char pipelines");
|
||||
if (terrainRenderer) terrainRenderer->recreatePipelines();
|
||||
if (waterRenderer) {
|
||||
waterRenderer->recreatePipelines();
|
||||
|
|
@ -758,7 +752,6 @@ void Renderer::applyMsaaChange() {
|
|||
if (wmoRenderer) wmoRenderer->recreatePipelines();
|
||||
if (m2Renderer) m2Renderer->recreatePipelines();
|
||||
if (characterRenderer) characterRenderer->recreatePipelines();
|
||||
LOG_WARNING("MSAA: recreating quest/weather/sky pipelines");
|
||||
if (questMarkerRenderer) questMarkerRenderer->recreatePipelines();
|
||||
if (weather) weather->recreatePipelines();
|
||||
if (lightning) lightning->recreatePipelines();
|
||||
|
|
@ -775,7 +768,6 @@ void Renderer::applyMsaaChange() {
|
|||
if (auto* lf = skySystem->getLensFlare()) lf->recreatePipelines();
|
||||
}
|
||||
|
||||
LOG_WARNING("MSAA: recreating minimap/postprocess/imgui");
|
||||
if (minimap) minimap->recreatePipelines();
|
||||
|
||||
// Selection circle + overlay + FSR use lazy init, just destroy them
|
||||
|
|
@ -804,7 +796,6 @@ void Renderer::applyMsaaChange() {
|
|||
};
|
||||
ImGui_ImplVulkan_Init(&initInfo);
|
||||
|
||||
LOG_WARNING("MSAA change complete");
|
||||
}
|
||||
|
||||
void Renderer::beginFrame() {
|
||||
|
|
@ -812,26 +803,15 @@ void Renderer::beginFrame() {
|
|||
if (!vkCtx) return;
|
||||
if (vkCtx->isDeviceLost()) return;
|
||||
|
||||
// Diagnostic: log pointer state to detect corrupt this/members
|
||||
LOG_WARNING("beginFrame: this=", (void*)this,
|
||||
" vkCtx=", (void*)vkCtx,
|
||||
" msaaP=", msaaChangePending_,
|
||||
" ppPipe=", (void*)postProcessPipeline_.get(),
|
||||
" cam=", (void*)camera.get(),
|
||||
" swDirty=", vkCtx->isSwapchainDirty());
|
||||
|
||||
// Apply deferred MSAA change between frames (before any rendering state is used)
|
||||
g_crashRenderPhase = "bf:msaa";
|
||||
if (msaaChangePending_) {
|
||||
applyMsaaChange();
|
||||
}
|
||||
|
||||
// Post-process resource management (§4.3 — delegates to PostProcessPipeline)
|
||||
g_crashRenderPhase = "bf:pp";
|
||||
if (postProcessPipeline_) postProcessPipeline_->manageResources();
|
||||
|
||||
// Handle swapchain recreation if needed
|
||||
g_crashRenderPhase = "bf:swap";
|
||||
if (vkCtx->isSwapchainDirty()) {
|
||||
(void)vkCtx->recreateSwapchain(window->getWidth(), window->getHeight());
|
||||
// Rebuild water resources that reference swapchain extent/views
|
||||
|
|
@ -843,7 +823,6 @@ void Renderer::beginFrame() {
|
|||
}
|
||||
|
||||
// Acquire swapchain image and begin command buffer
|
||||
g_crashRenderPhase = "bf:acquire";
|
||||
currentCmd = vkCtx->beginFrame(currentImageIndex);
|
||||
if (currentCmd == VK_NULL_HANDLE) {
|
||||
// Swapchain out of date, will retry next frame
|
||||
|
|
@ -851,20 +830,13 @@ void Renderer::beginFrame() {
|
|||
}
|
||||
|
||||
// FSR2 jitter pattern (§4.3 — delegates to PostProcessPipeline)
|
||||
g_crashRenderPhase = "bf:jitter";
|
||||
if (postProcessPipeline_ && camera) postProcessPipeline_->applyJitter(camera.get());
|
||||
|
||||
// Update per-frame UBO with current camera/lighting state
|
||||
g_crashRenderPhase = "bf:ubo";
|
||||
updatePerFrameUBO();
|
||||
|
||||
// GPU crash diagnostic: skip all pre-passes to isolate crash source
|
||||
static const bool skipPrePasses = (std::getenv("WOWEE_SKIP_PREPASSES") != nullptr);
|
||||
|
||||
if (!skipPrePasses) {
|
||||
// --- Off-screen pre-passes (before main render pass) ---
|
||||
// Minimap composite (renders 3x3 tile grid into 768x768 render target)
|
||||
g_crashRenderPhase = "bf:minimap";
|
||||
if (minimap && minimap->isEnabled() && camera) {
|
||||
glm::vec3 minimapCenter = camera->getPosition();
|
||||
if (cameraController && cameraController->isThirdPerson())
|
||||
|
|
@ -872,13 +844,11 @@ void Renderer::beginFrame() {
|
|||
minimap->compositePass(currentCmd, minimapCenter);
|
||||
}
|
||||
// World map composite (renders zone tiles into 1024x768 render target)
|
||||
g_crashRenderPhase = "bf:worldmap";
|
||||
if (worldMap) {
|
||||
worldMap->compositePass(currentCmd);
|
||||
}
|
||||
|
||||
// Character preview composite passes
|
||||
g_crashRenderPhase = "bf:preview";
|
||||
for (auto* preview : activePreviews_) {
|
||||
if (preview && preview->isModelLoaded()) {
|
||||
preview->compositePass(currentCmd, vkCtx->getCurrentFrame());
|
||||
|
|
@ -886,18 +856,14 @@ void Renderer::beginFrame() {
|
|||
}
|
||||
|
||||
// Shadow pre-pass (before main render pass)
|
||||
g_crashRenderPhase = "bf:shadow";
|
||||
if (shadowsEnabled && shadowDepthImage[0] != VK_NULL_HANDLE) {
|
||||
renderShadowPass();
|
||||
}
|
||||
|
||||
// Water reflection pre-pass (renders scene from mirrored camera into 512x512 texture)
|
||||
g_crashRenderPhase = "bf:reflection";
|
||||
renderReflectionPass();
|
||||
} // !skipPrePasses
|
||||
|
||||
// --- Begin render pass ---
|
||||
g_crashRenderPhase = "bf:renderpass";
|
||||
// Select framebuffer: PP off-screen target or swapchain (§4.3 — PostProcessPipeline)
|
||||
VkRenderPassBeginInfo rpInfo{};
|
||||
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
|
|
|
|||
|
|
@ -270,7 +270,8 @@ bool VkContext::createInstance(SDL_Window* window) {
|
|||
vkb::InstanceBuilder builder;
|
||||
builder.set_app_name("Wowee")
|
||||
.set_app_version(VK_MAKE_VERSION(1, 0, 0))
|
||||
.require_api_version(1, 1, 0);
|
||||
.require_api_version(1, 2, 0)
|
||||
.set_minimum_instance_version(1, 1, 0);
|
||||
|
||||
for (auto ext : sdlExts) {
|
||||
builder.enable_extension(ext);
|
||||
|
|
@ -291,7 +292,14 @@ bool VkContext::createInstance(SDL_Window* window) {
|
|||
instance = vkbInstance_.instance;
|
||||
debugMessenger = vkbInstance_.debug_messenger;
|
||||
|
||||
LOG_INFO("Vulkan instance created");
|
||||
// Query the actual instance API version for gating core 1.2+ calls
|
||||
uint32_t instVer = VK_API_VERSION_1_1;
|
||||
if (vkEnumerateInstanceVersion(&instVer) != VK_SUCCESS)
|
||||
instVer = VK_API_VERSION_1_1;
|
||||
instanceApiVersion_ = instVer;
|
||||
LOG_INFO("Vulkan instance created (instance API version: ",
|
||||
VK_VERSION_MAJOR(instVer), ".", VK_VERSION_MINOR(instVer), ".",
|
||||
VK_VERSION_PATCH(instVer), ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -337,7 +345,10 @@ bool VkContext::selectPhysicalDevice() {
|
|||
props2.pNext = &dsResolveProps;
|
||||
vkGetPhysicalDeviceProperties2(physicalDevice, &props2);
|
||||
|
||||
if (apiVersion >= VK_API_VERSION_1_2) {
|
||||
// Gate on instance API version — vkCreateRenderPass2 is core 1.2 and only
|
||||
// available when the instance was created with apiVersion >= 1.2.
|
||||
// The device may report 1.2+ but a 1.1 instance won't have the function pointer.
|
||||
if (instanceApiVersion_ >= VK_API_VERSION_1_2) {
|
||||
VkResolveModeFlags modes = dsResolveProps.supportedDepthResolveModes;
|
||||
if (modes & VK_RESOLVE_MODE_SAMPLE_ZERO_BIT) {
|
||||
depthResolveMode_ = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
|
||||
|
|
@ -467,7 +478,7 @@ bool VkContext::createAllocator() {
|
|||
allocInfo.instance = instance;
|
||||
allocInfo.physicalDevice = physicalDevice;
|
||||
allocInfo.device = device;
|
||||
allocInfo.vulkanApiVersion = VK_API_VERSION_1_1;
|
||||
allocInfo.vulkanApiVersion = instanceApiVersion_;
|
||||
|
||||
if (vmaCreateAllocator(&allocInfo, &allocator) != VK_SUCCESS) {
|
||||
LOG_ERROR("Failed to create VMA allocator");
|
||||
|
|
|
|||
|
|
@ -48,10 +48,6 @@
|
|||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
// Signal-safe render-phase marker — crash handler reads this to identify which
|
||||
// render call was active when a SIGSEGV occurs (backtrace is unreliable with
|
||||
// -fomit-frame-pointer).
|
||||
volatile const char* g_crashRenderPhase = "idle";
|
||||
#include <unordered_set>
|
||||
|
||||
namespace {
|
||||
|
|
@ -303,7 +299,6 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
// Process targeting input before UI windows
|
||||
processTargetInput(gameHandler);
|
||||
|
||||
g_crashRenderPhase = "playerFrame";
|
||||
renderPlayerFrame(gameHandler);
|
||||
|
||||
// Pet frame (below player frame, only when player has an active pet)
|
||||
|
|
@ -358,7 +353,6 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
}
|
||||
|
||||
// ---- New UI elements ----
|
||||
g_crashRenderPhase = "actionBar";
|
||||
actionBarPanel_.renderActionBar(gameHandler, settingsPanel_, chatPanel_,
|
||||
inventoryScreen, spellbookScreen, questLogScreen,
|
||||
[this](uint32_t id, pipeline::AssetManager* am) { return getSpellIcon(id, am); });
|
||||
|
|
@ -368,43 +362,31 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
actionBarPanel_.renderXpBar(gameHandler, settingsPanel_);
|
||||
actionBarPanel_.renderRepBar(gameHandler, settingsPanel_);
|
||||
auto spellIconFn = [this](uint32_t id, pipeline::AssetManager* am) { return getSpellIcon(id, am); };
|
||||
g_crashRenderPhase = "castBar";
|
||||
combatUI_.renderCastBar(gameHandler, spellIconFn);
|
||||
renderMirrorTimers(gameHandler);
|
||||
combatUI_.renderCooldownTracker(gameHandler, settingsPanel_, spellIconFn);
|
||||
renderQuestObjectiveTracker(gameHandler);
|
||||
g_crashRenderPhase = "nameplates";
|
||||
renderNameplates(gameHandler); // player names always shown; NPC plates gated by showNameplates_
|
||||
g_crashRenderPhase = "bgScore";
|
||||
combatUI_.renderBattlegroundScore(gameHandler);
|
||||
combatUI_.renderRaidWarningOverlay(gameHandler);
|
||||
combatUI_.renderCombatText(gameHandler);
|
||||
combatUI_.renderDPSMeter(gameHandler, settingsPanel_);
|
||||
g_crashRenderPhase = "durability";
|
||||
renderDurabilityWarning(gameHandler);
|
||||
renderUIErrors(gameHandler, ImGui::GetIO().DeltaTime);
|
||||
toastManager_.renderEarlyToasts(ImGui::GetIO().DeltaTime, gameHandler);
|
||||
g_crashRenderPhase = "partyFrames";
|
||||
if (socialPanel_.showRaidFrames_) {
|
||||
socialPanel_.renderPartyFrames(gameHandler, chatPanel_, spellIconFn);
|
||||
}
|
||||
g_crashRenderPhase = "bossFrames";
|
||||
socialPanel_.renderBossFrames(gameHandler, spellbookScreen, spellIconFn);
|
||||
g_crashRenderPhase = "dialogs";
|
||||
dialogManager_.renderDialogs(gameHandler, inventoryScreen, chatPanel_);
|
||||
g_crashRenderPhase = "guildRoster";
|
||||
socialPanel_.renderGuildRoster(gameHandler, chatPanel_);
|
||||
socialPanel_.renderSocialFrame(gameHandler, chatPanel_);
|
||||
g_crashRenderPhase = "buffBar";
|
||||
combatUI_.renderBuffBar(gameHandler, spellbookScreen, spellIconFn);
|
||||
g_crashRenderPhase = "lootWindow";
|
||||
windowManager_.renderLootWindow(gameHandler, inventoryScreen, chatPanel_);
|
||||
windowManager_.renderGossipWindow(gameHandler, chatPanel_);
|
||||
g_crashRenderPhase = "questWindows";
|
||||
windowManager_.renderQuestDetailsWindow(gameHandler, chatPanel_, inventoryScreen);
|
||||
windowManager_.renderQuestRequestItemsWindow(gameHandler, chatPanel_, inventoryScreen);
|
||||
windowManager_.renderQuestOfferRewardWindow(gameHandler, chatPanel_, inventoryScreen);
|
||||
g_crashRenderPhase = "vendorTrainer";
|
||||
windowManager_.renderVendorWindow(gameHandler, inventoryScreen, chatPanel_);
|
||||
windowManager_.renderTrainerWindow(gameHandler,
|
||||
[this](uint32_t id, pipeline::AssetManager* am) { return getSpellIcon(id, am); });
|
||||
|
|
@ -416,48 +398,36 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
windowManager_.renderBankWindow(gameHandler, inventoryScreen, chatPanel_);
|
||||
windowManager_.renderGuildBankWindow(gameHandler, inventoryScreen, chatPanel_);
|
||||
windowManager_.renderAuctionHouseWindow(gameHandler, inventoryScreen, chatPanel_);
|
||||
g_crashRenderPhase = "dungeonFinder";
|
||||
socialPanel_.renderDungeonFinderWindow(gameHandler, chatPanel_);
|
||||
windowManager_.renderInstanceLockouts(gameHandler);
|
||||
socialPanel_.renderWhoWindow(gameHandler, chatPanel_);
|
||||
g_crashRenderPhase = "combatLog";
|
||||
combatUI_.renderCombatLog(gameHandler, spellbookScreen);
|
||||
g_crashRenderPhase = "achievementSkills";
|
||||
windowManager_.renderAchievementWindow(gameHandler);
|
||||
windowManager_.renderSkillsWindow(gameHandler);
|
||||
windowManager_.renderTitlesWindow(gameHandler);
|
||||
windowManager_.renderEquipSetWindow(gameHandler);
|
||||
windowManager_.renderGmTicketWindow(gameHandler);
|
||||
g_crashRenderPhase = "inspectBook";
|
||||
socialPanel_.renderInspectWindow(gameHandler, inventoryScreen);
|
||||
windowManager_.renderBookWindow(gameHandler);
|
||||
g_crashRenderPhase = "threatBg";
|
||||
combatUI_.renderThreatWindow(gameHandler);
|
||||
combatUI_.renderBgScoreboard(gameHandler);
|
||||
g_crashRenderPhase = "minimap";
|
||||
if (showMinimap_) {
|
||||
renderMinimapMarkers(gameHandler);
|
||||
}
|
||||
g_crashRenderPhase = "deathLogout";
|
||||
windowManager_.renderLogoutCountdown(gameHandler);
|
||||
windowManager_.renderDeathScreen(gameHandler);
|
||||
windowManager_.renderReclaimCorpseButton(gameHandler);
|
||||
dialogManager_.renderLateDialogs(gameHandler);
|
||||
chatPanel_.renderBubbles(gameHandler);
|
||||
windowManager_.renderEscapeMenu(settingsPanel_);
|
||||
g_crashRenderPhase = "settings";
|
||||
settingsPanel_.renderSettingsWindow(inventoryScreen, chatPanel_, [this]() { saveSettings(); });
|
||||
toastManager_.renderLateToasts(gameHandler);
|
||||
g_crashRenderPhase = "weather";
|
||||
renderWeatherOverlay(gameHandler);
|
||||
|
||||
g_crashRenderPhase = "worldMap";
|
||||
renderWorldMap(gameHandler);
|
||||
|
||||
g_crashRenderPhase = "questLog";
|
||||
questLogScreen.render(gameHandler, inventoryScreen);
|
||||
|
||||
g_crashRenderPhase = "spellbook";
|
||||
spellbookScreen.render(gameHandler, services_.assetManager);
|
||||
|
||||
// Insert spell link into chat if player shift-clicked a spellbook entry
|
||||
|
|
@ -510,7 +480,6 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
windowManager_.vendorBagsOpened_ = false;
|
||||
}
|
||||
|
||||
g_crashRenderPhase = "inventory";
|
||||
inventoryScreen.setGameHandler(&gameHandler);
|
||||
inventoryScreen.render(gameHandler.getInventory(), gameHandler.getMoneyCopper());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue