Commit graph

3150 commits

Author SHA1 Message Date
Kelsi Rae Davis
50fdfd2e22
Merge pull request #47 from sschepens/patch-2
refactor asset extractor
2026-04-05 01:10:28 -07:00
Kelsi Rae Davis
f9d6ae5ef1
Merge pull request #46 from ldmonster/feat/animation-handling
[feat] animation: Comprehensive Animation System Overhaul — 452 Constants, 30-Phase State Machine
2026-04-05 01:09:51 -07:00
Kelsi Rae Davis
6d60717545
Merge pull request #45 from ldmonster/feat/rendering-performance-architecture
[feat] Rendering Architecture & GPU Performance + Bug Fixes
2026-04-05 01:09:03 -07:00
Paul
e58f9b4b40 feat(animation): 452 named constants, 30-phase character animation state machine
Add animation_ids.hpp/cpp with all 452 WoW animation ID constants (anim::STAND,
anim::RUN, anim::FIRE_BOW, ... anim::FLY_BACKWARDS, etc.), nameFromId() O(1)
lookup, and flyVariant() compact 218-element ground→FLY_* resolver.

Expand AnimationController into a full state machine with 20+ named states:
spell cast (directed→omni→cast fallback chain, instant one-shot release),
hit reactions (WOUND/CRIT/DODGE/BLOCK/SHIELD_BLOCK), stun, wounded idle,
stealth animation substitution, loot, fishing channel, sit/sleep/kneel
down→loop→up transitions, sheathe/unsheathe combat enter/exit, ranged weapons
(BOW/GUN/CROSSBOW/THROWN with reload states), game object OPEN/CLOSE/DESTROY,
vehicle enter/exit, mount flight directionals (FLY_LEFT/RIGHT/UP/DOWN/BACKWARDS),
emote state variants, off-hand/pierce/dual-wield alternation, NPC
birth/spawn/drown/rise, sprint aura override, totem idle, NPC greeting/farewell.

Add spell_defines.hpp with SpellEffect (~45 constants) and SpellMissInfo
(12 constants) namespaces; replace all magic numbers in spell_handler.cpp.

Add GAMEOBJECT_BYTES_1 to update field table (all 4 expansion JSONs) and wire
GameObjectStateCallback. Add DBC cross-validation on world entry.

Expand tools/_ANIM_NAMES from ~35 to 452 entries in m2_viewer.py and
asset_pipeline_gui.py. Add tests/test_animation_ids.cpp.

Bug fixes included:
- Stand state 1 was animating READY_2H(27) — fixed to SITTING(97)
- Spell casts ended freeze-frame — add one-shot release animation
- NPC 2H swing probe chain missing ATTACK_2H_LOOSE (polearm/staff)
- Chair sits (states 2/4/5/6) incorrectly played floor-sit transition
- STOP(3) used for all spell casts — replaced with model-aware chain
2026-04-04 23:02:53 +03:00
sschepens
a381598bf8
fix gitignore 2026-04-04 15:29:50 -03:00
sschepens
1e464dd513
refactor path mapper 2026-04-04 14:34:23 -03:00
sschepens
5542cbaa02
refactor asset extractor
- mpq and locale finding is now case insensitive
- improve extraction order and support more patches
- unified much of the mpq logic for all expansions
- return a list of ordered paths for loading
2026-04-04 14:00:55 -03:00
Paul
d54e262048 feat(rendering): GPU architecture + visual quality fixes
M2 GPU instancing
- M2InstanceGPU SSBO (96 B/entry, double-buffered, 16384 max)
- Group opaque instances by (modelId, LOD); single vkCmdDrawIndexed per group
- boneBase field indexes into mega bone SSBO via gl_InstanceIndex

Indirect terrain drawing
- 24 MB mega index buffer (6M uint32) + 64 MB mega vertex buffer
- CPU builds VkDrawIndexedIndirectCommand per visible chunk
- Single VB/IB bind per frame; shadow pass reuses mega buffers
- Replaced vkCmdDrawIndexedIndirect with direct vkCmdDrawIndexed to fix
  host-mapped buffer race condition that caused terrain flickering

GPU frustum culling (compute shader)
- m2_cull.comp.glsl: 64-thread workgroups, sphere-vs-6-planes + distance cull
- CullInstanceGPU SSBO input, uint visibility[] output, double-buffered
- dispatchCullCompute() runs before main pass via render graph node

Consolidated bone matrix SSBOs
- 16 MB double-buffered mega bone SSBO (2048 instances × 128 bones)
- Eliminated per-instance descriptor sets; one megaBoneSet_ per frame
- prepareRender() packs bone matrices consecutively into current frame slot

Render graph / frame graph
- RenderGraph: RGResource handles, RGPass nodes, Kahn topological sort
- Automatic VkImageMemoryBarrier/VkBufferMemoryBarrier between passes
- Passes: minimap_composite, worldmap_composite, preview_composite,
  shadow_pass, reflection_pass, compute_cull
- beginFrame() uses buildFrameGraph() + renderGraph_->execute(cmd)

Pipeline derivatives
- PipelineBuilder::setFlags/setBasePipeline for VK_PIPELINE_CREATE_DERIVATIVE_BIT
- M2 opaque = base; alphaTest/alpha/additive are derivatives
- Applied to terrain (wireframe) and WMO (alpha-test) renderers

Rendering bug fixes:
- fix(shadow): compute lightSpaceMatrix before updatePerFrameUBO to eliminate
  one-frame lag that caused shadow trails and flicker on moving objects
- fix(shadow): scale depth bias with shadowDistance_ instead of hardcoded 0.8f
  to prevent acne at close range and gaps at far range
- fix(visibility): WMO group distance threshold 500u → 1200u to match terrain
  view distance; buildings were disappearing on the horizon
- fix(precision): camera near plane 0.05 → 0.5 (ratio 600K:1 → 60K:1),
  eliminating Z-fighting and improving frustum plane extraction stability
- fix(streaming): terrain load radius 4 → 6 tiles (~2133u → ~3200u) to exceed
  M2 render distance (2800u) and eliminate pop-in when camera turns;
  unload radius 7 → 9; spawn radius 3 → 4
- fix(visibility): ground-detail M2 distance multiplier 0.75 → 0.9 to reduce
  early pop of grass and debris
2026-04-04 13:43:16 +03:00
Pavel Okhlopkov
ca3cea078b
Merge branch 'Kelsidavis:master' into master 2026-04-04 13:42:02 +03:00
Kelsi Davis
2343b768ce fix: warden mmap on macOS, add external listfile support to asset extractor
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
Drop PROT_EXEC from warden module mmap when using Unicorn emulation
(not needed — module image is copied into emulator address space). Use
MAP_JIT on macOS for the native fallback path.

Add --listfile option to asset_extract and SFileAddListFileEntries
support for resolving unnamed MPQ hash table entries from external
listfiles.
2026-04-04 01:16:28 -07:00
Kelsi Davis
2fd9473f3b fix(rendering): alpha-to-coverage for hair, skip eye glow geosets, add missing include
- Enable alpha-to-coverage on alphaTestPipeline for smooth hair edges
  when MSAA is active (both init and recreatePipelines paths)
- Shader uses fwidth()-based alpha rescaling for clean coverage
- Skip group 17/18 geosets (DK/NE eye glow) when no geoset filter is
  set — prevents blue eye glow on all NPCs
- Add missing <libgen.h> include for dirname() on Linux
2026-04-04 01:16:28 -07:00
Kelsi Davis
f577411a15 fix(chat): resolve /r reply target when name arrives after whisper
Whisper sender name may not be in the player name cache when the packet
arrives. Store the sender GUID and lazily resolve the name from the
cache in getLastWhisperSender(). Also backfill lastWhisperSender_ when
the SMSG_NAME_QUERY_RESPONSE arrives.
2026-04-04 01:16:28 -07:00
Kelsi Davis
3f408341e1 fix(rendering): correct alpha test on opaque batches and hair transparency
- alphaTestPipeline_ uses blendDisabled() so surviving pixels are fully
  opaque (was blendAlpha, causing hair to blend with background)
- Remove alphaCutout from alphaTest condition — opaque materials like
  capes no longer alpha-test just because their texture has an alpha
  channel
- Two-pass batch rendering: opaque (blendMode 0) draws first to
  establish depth, then alpha-key/blend draws on top
2026-04-04 01:16:28 -07:00
Kelsi Davis
c95147390b fix(rendering,game): init bone SSBO to identity; stop movement before cast
Bone SSBO buffers were allocated for MAX_BONES (240) entries but only
the first numBones were written. Uninitialized GPU memory in the
remaining slots caused vertex spikes when any bone index exceeded the
model's actual bone count.

Also send MSG_MOVE_STOP before spell casts so the server doesn't reject
cast-time spells (e.g. hearthstone) with "can't do that while moving".
2026-04-04 01:16:28 -07:00
Kelsi Davis
bde9bd20d8 fix(rendering): use separate timer for global sequence bones
Global sequence bones (hair, cape, physics) need time values spanning
their full duration (up to ~968733ms), but animationTime wraps at the
current animation's sequence duration (~2000ms for walk). This caused
vertex spikes projecting from fingers/neck/ponytail as bones got stuck
in the first ~2s of their loop. Add a separate globalSequenceTime
accumulator that is not wrapped at the animation duration.
2026-04-04 01:16:28 -07:00
Kelsi Davis
f520511139 fix: chdir to executable directory at startup for relative asset paths
The binary assumed it was always launched from its own directory, causing
shader/asset loads to fail when run from any other working directory.
2026-04-04 01:16:28 -07:00
k
b3fa8cf5f3 fix: warden mmap on macOS, add external listfile support to asset extractor
Drop PROT_EXEC from warden module mmap when using Unicorn emulation
(not needed — module image is copied into emulator address space). Use
MAP_JIT on macOS for the native fallback path.

Add --listfile option to asset_extract and SFileAddListFileEntries
support for resolving unnamed MPQ hash table entries from external
listfiles.
2026-04-04 00:22:07 -07:00
Kelsi
84108c44f5 fix(rendering): alpha-to-coverage for hair, skip eye glow geosets, add missing include
- Enable alpha-to-coverage on alphaTestPipeline for smooth hair edges
  when MSAA is active (both init and recreatePipelines paths)
- Shader uses fwidth()-based alpha rescaling for clean coverage
- Skip group 17/18 geosets (DK/NE eye glow) when no geoset filter is
  set — prevents blue eye glow on all NPCs
- Add missing <libgen.h> include for dirname() on Linux
2026-04-04 00:21:15 -07:00
Kelsi
5538655383 fix(chat): resolve /r reply target when name arrives after whisper
Whisper sender name may not be in the player name cache when the packet
arrives. Store the sender GUID and lazily resolve the name from the
cache in getLastWhisperSender(). Also backfill lastWhisperSender_ when
the SMSG_NAME_QUERY_RESPONSE arrives.
2026-04-04 00:03:19 -07:00
Kelsi
c85d023329 fix(rendering): correct alpha test on opaque batches and hair transparency
- alphaTestPipeline_ uses blendDisabled() so surviving pixels are fully
  opaque (was blendAlpha, causing hair to blend with background)
- Remove alphaCutout from alphaTest condition — opaque materials like
  capes no longer alpha-test just because their texture has an alpha
  channel
- Two-pass batch rendering: opaque (blendMode 0) draws first to
  establish depth, then alpha-key/blend draws on top
2026-04-04 00:03:19 -07:00
Kelsi
100394a743 fix(rendering,game): init bone SSBO to identity; stop movement before cast
Bone SSBO buffers were allocated for MAX_BONES (240) entries but only
the first numBones were written. Uninitialized GPU memory in the
remaining slots caused vertex spikes when any bone index exceeded the
model's actual bone count.

Also send MSG_MOVE_STOP before spell casts so the server doesn't reject
cast-time spells (e.g. hearthstone) with "can't do that while moving".
2026-04-04 00:03:19 -07:00
Kelsi
aeb295e0bb fix(rendering): use separate timer for global sequence bones
Global sequence bones (hair, cape, physics) need time values spanning
their full duration (up to ~968733ms), but animationTime wraps at the
current animation's sequence duration (~2000ms for walk). This caused
vertex spikes projecting from fingers/neck/ponytail as bones got stuck
in the first ~2s of their loop. Add a separate globalSequenceTime
accumulator that is not wrapped at the animation duration.
2026-04-04 00:03:19 -07:00
k
b54458fe6c fix: chdir to executable directory at startup for relative asset paths
The binary assumed it was always launched from its own directory, causing
shader/asset loads to fail when run from any other working directory.
2026-04-03 23:27:13 -07:00
Kelsi
f79395788a fix(rendering): filter player hair geosets via CharHairGeosets.dbc
buildDefaultPlayerGeosets() was inserting all submeshIds 0-99 into
activeGeosets, showing every hair variation simultaneously. Now uses
the hairGeosetMap_ (from CharHairGeosets.dbc) to select only the
correct hair scalp geoset for the player's race/sex/style, matching
the existing NPC geoset filtering logic in EntitySpawner.
2026-04-03 22:43:37 -07:00
Kelsi
5468a93f2e fix(rendering): handle global sequences in character bone transforms
Hair, cape, and other physics bones use global sequences (continuously
looping timers independent of the character's current animation). The
character renderer was ignoring globalSequence entirely, causing these
bones to fall back to identity transforms and produce deformed/spiked
hair geometry. Added resolveTrackTime() to wrap global sequence time
correctly, matching the M2 renderer's existing behavior.
2026-04-03 22:37:46 -07:00
Kelsi
634bac6c7a Revert "fix(rendering): remap M2 vertex bone indices through bone lookup table"
This reverts commit 04ad88330f.
2026-04-03 22:26:14 -07:00
Kelsi
04ad88330f fix(rendering): remap M2 vertex bone indices through bone lookup table
M2 vertex bone indices are indices into boneLookupTable, not direct bone
array indices. Without remapping, vertices weighted to higher bone
indices (cloak, cape, hair) get the wrong bone transform, causing
vertices to project wildly outward from the character.
2026-04-03 22:22:51 -07:00
Kelsi
1feb6ea63f fix(rendering): sync async upload batches before rendering
Wait on in-flight upload batch fences at the start of each frame and
insert a memory barrier (transfer→fragment shader) so the graphics
queue sees completed layout transitions from the transfer queue.
Fixes VK_IMAGE_LAYOUT_UNDEFINED validation errors for freshly loaded
textures.
2026-04-03 22:09:41 -07:00
Kelsi
1379e74c40 fix(dbc): runtime detection for ItemDisplayInfo texture field indices
Revert static JSON layout changes (15-22 back to 14-21) since WotLK
loads the Classic 23-field DBC. Add getItemDisplayInfoTextureFields()
helper that detects field count at runtime and adjusts the texture
base index accordingly (14 for 23-field, 15 for 25-field).
2026-04-03 22:05:38 -07:00
Kelsi
3111fa50e8 fix(dbc): correct ItemDisplayInfo texture field indices for TBC/WotLK
TBC and WotLK have 25-field ItemDisplayInfo.dbc (vs 23 in Classic/Turtle)
with 2 extra fields before the texture region block. The texture fields
start at index 15 (not 14), shifting all 8 regions by +1.

This caused every equipment texture to be composited into the wrong body
region — e.g. LegLower textures landing in FootTexture, belt textures in
LegLowerTexture instead of LegUpperTexture ("everything shifted by 1").

Verified against raw DBC binary data: Classic field[14]=ArmUpper,
TBC/WotLK field[15]=ArmUpper.
2026-04-03 21:52:20 -07:00
Kelsi
44df2a1e28 chore: track FidelityFX-SDK and FidelityFX-FSR2 as submodules
Both repos were previously untracked clones under extern/ that users had
to fetch manually. Adding them as submodules ensures git pull --recurse
fetches the correct commits with our format-matching patches.

- FidelityFX-FSR2: standalone FSR2 v2.2.1 upscaler (Kelsidavis fork)
- FidelityFX-SDK: full SDK with FSR3 frame generation (Kelsidavis fork)
2026-04-03 21:45:42 -07:00
Kelsi
746ac25c14 fix(rendering): prevent MSAA+FSR2 framebuffer mismatch crash
When saved settings loaded MSAA before FSR2 on startup, the pending MSAA
change (e.g. 8x) was queued before FSR2 was enabled. Since FSR2 checked
the current MSAA (still 1x), it didn't override the pending change.
On the next frame, applyMsaaChange created a 4-attachment MSAA render pass,
then FSR2 lazily created a 2-attachment framebuffer against it — SIGSEGV.

Add guards in both applyMsaaChange (force 1x if FSR2 is blocking) and
setFSR2Enabled (override any pending MSAA >1x when enabling FSR2).
2026-04-03 21:41:14 -07:00
Kelsi
7264ba1706 fix(extractor): lowercase all output paths to prevent duplicate folders
WoW archives contain mixed-case variants of the same path (e.g.,
ARMLOWERTEXTURE vs ArmLowerTexture) which created duplicate directories
on case-sensitive Linux filesystems. Now mapPath() lowercases the entire
output. Also keeps TextureComponents and ObjectComponents directory
names instead of abbreviating them (item/texturecomponents/ instead of
item/texture/) so filesystem paths match the WoW virtual paths used in
manifest lookups.
2026-04-03 21:26:20 -07:00
Kelsi
23bda2d476 fix(vulkan): enable missing device features for FSR2 compute shaders
AMD RADV validation flagged missing shaderStorageImageWriteWithoutFormat,
shaderInt16, shaderFloat16, and deviceCoherentMemory. The first two are
now required device features; shaderFloat16 is optionally enabled via
Vulkan 1.2 feature query; AMD device coherent memory extension and
feature are enabled when available to prevent VMA memory type errors.
2026-04-03 21:20:37 -07:00
Kelsi
161b218fa1 fix(rendering): FSR1/FXAA paths not signaling inline mode to endFrame
executePostProcessing() only set inlineMode=true in the FSR2 path.
The FXAA and FSR1 paths both start INLINE render passes but returned
false, causing endFrame() to record ImGui into a secondary command
buffer and execute it inside the INLINE pass — validation errors and
UI disappearing on AMD RADV when FSR1+MSAA are both enabled.
2026-04-03 21:13:35 -07:00
Kelsi
17c16150d6 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
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.
2026-04-03 20:58:32 -07:00
Kelsi
9c4e61a227 fix(diagnostics): instrument applyMsaaChange to find NULL deref
AMD crash is caused by msaaChangePending_ flipping true from saved settings.
applyMsaaChange() then crashes with faultAddr=(nil). Add LOG_WARNING markers
between pipeline recreation groups to identify the failing call.
2026-04-03 20:42:08 -07:00
Kelsi
45ac7e4d8e fix(diagnostics): log renderer state on each beginFrame for AMD crash
Add per-frame LOG_WARNING with this/vkCtx/camera/postProcess pointers and
msaaChangePending state. If the log prints before crash, the pointer values
tell us what's corrupt. If it doesn't print, crash is in the log itself
(meaning this or vkCtx is corrupt).
2026-04-03 20:37:21 -07:00
Kelsi
cd07e23485 fix(diagnostics): finer beginFrame sub-phase markers for AMD crash
Previous markers showed crash before bf:ubo. Add markers at bf:msaa,
bf:pp, bf:swap, bf:acquire, bf:jitter to isolate which early beginFrame
call does the NULL deref.
2026-04-03 20:32:18 -07:00
Kelsi
5778ba230d fix(diagnostics): add sub-phase markers inside Renderer::beginFrame
AMD RADV crash is renderPhase=beginFrame with faultAddr=(nil) — a NULL
pointer dereference somewhere in the pre-pass chain. Add granular markers
(bf:ubo, bf:minimap, bf:worldmap, bf:preview, bf:shadow, bf:reflection,
bf:renderpass) to pinpoint the exact call.
2026-04-03 20:28:37 -07:00
Kelsi
82267320b0 fix(diagnostics): add render-phase crash markers and improve signal handling
Add signal-safe render-phase markers throughout GameScreen::render() and
Application::render() so the crash handler can report which render call was
active when a SIGSEGV occurs. The AMD RADV crash backtrace only shows 2
frames due to missing frame pointers, making it impossible to identify the
actual crash site.

Changes:
- Add volatile g_crashRenderPhase marker updated before each major render call
- Upgrade Linux signal handler to sigaction with SA_SIGINFO for faulting address
- Set ImGui CheckVkResultFn to log silent Vulkan errors in ImGui backend
- Enable -fno-omit-frame-pointer in all build configs (not just Debug/RelWithDebInfo)
2026-04-03 20:19:33 -07:00
Kelsi
b092bc2e90 fix(rendering): use deferAfterAllFrameFences for bone destruction
destroyInstanceBones loops over both frame slots (i=0,1), deferring
both boneSet[0] and boneSet[1] to the current frame's fence. When
currentFrame=0, boneSet[1] is freed after only slot 0's fence completes
while slot 1's command buffer may still be using it.

Switch both M2Renderer and CharacterRenderer bone destruction from
deferAfterFrameFence to deferAfterAllFrameFences to ensure all
in-flight frames have completed before freeing cross-slot resources.
2026-04-03 19:54:54 -07:00
Kelsi
3ac8c4d95f fix(rendering): wait all frame fences before freeing shared descriptor sets
deferAfterFrameFence only waits for one frame slot's fence, but shared
resources (material descriptor sets, vertex/index buffers) are bound by
both in-flight frames' command buffers. On AMD RADV this caused
vkFreeDescriptorSets errors and eventual SIGSEGV.

Add deferAfterAllFrameFences: queues to every frame slot with a shared
counter so cleanup runs exactly once, after the last slot is fenced.
Use it for WMO, terrain, water, and character model shared resources.
Per-frame bone sets keep using deferAfterFrameFence (already correct).

Also fix character renderer vertex format: R8G8B8A8_UINT -> _SINT to
match shader's ivec4 input (RADV validation rejects the mismatch).
2026-04-03 19:48:43 -07:00
Kelsi
def821055b fix(parsing): validate spline endPoint coords to reject false-positive format matches
The WotLK spline parser tries 6 format variants and accepts the first
that passes minimal validation (pointCount<=256, splineMode<=3). A wrong
format can pass by coincidence, consuming incorrect bytes and corrupting
all subsequent UPDATE_OBJECT blocks (e.g. maskBlockCount=219 garbage).

Add endPoint coordinate validation: reject spline parses where the
endpoint is non-finite or outside world bounds (65k). Also harden the
Turtle parser to keep successfully-parsed blocks on mid-packet failure
instead of discarding the entire packet.
2026-04-03 19:36:34 -07:00
Kelsi
40e72d535e fix(rendering): defer model buffer destruction and per-frame FXAA descriptors
CharacterRenderer::destroyModelGPU now defers vertex/index buffer
destruction when replacing models mid-stream, preventing use-after-free
on AMD RADV. FXAA descriptor sets are now per-frame to eliminate
write-read races between in-flight command buffers. Water reflection
descriptor update narrowed to current frame only.
2026-04-03 19:17:55 -07:00
Kelsi
e19bf76d88 fix(rendering): defer character renderer bone descriptor destruction
CharacterRenderer::destroyInstanceBones had the same immediate-free bug
as M2Renderer — freeing bone descriptor sets and buffers while in-flight
command buffers still reference them. Applies the same deferred pattern
via deferAfterFrameFence for the removeInstance streaming path.
2026-04-03 18:53:06 -07:00
Kelsi
345b41b810 fix(auction): resolve item GUID with fallback and gate packet format
auctionSellItem now resolves the item GUID internally via
backpackSlotGuids_ with resolveOnlineItemGuid fallback, matching the
pattern used by vendor sell and item use. Previously the UI passed
the GUID directly from getBackpackItemGuid() with no fallback, so
items with unset slot GUIDs silently failed to list.

Also gates CMSG_AUCTION_SELL_ITEM format by expansion: Classic/TBC
omits the itemCount and stackCount fields that WotLK requires.
2026-04-03 18:46:49 -07:00
Kelsi
ac5c61203d fix(rendering): defer descriptor set destruction during streaming unload
M2 destroyInstanceBones and WMO destroyGroupGPU freed descriptor sets
and buffers immediately during tile streaming, while in-flight command
buffers still referenced them — causing DEVICE_LOST on AMD RADV.

Now defers GPU resource destruction via deferAfterFrameFence in streaming
paths (removeInstance, removeInstances, unloadModel). Immediate
destruction preserved for shutdown/clear paths that vkDeviceWaitIdle
first.

Also: vkDeviceWaitIdle before WMO backfillNormalMaps descriptor rebinds,
and fillModeNonSolid added to required device features for wireframe
pipelines on AMD.
2026-04-03 18:30:52 -07:00
Kelsi
8fd4dccf6b fix(vendor): preserve repair flag across ListInventory parse
ListInventoryParser::parse() was resetting the entire ListInventoryData
struct, wiping the canRepair flag set by the gossip handler before the
server response arrived. Preserve it across the parse.

Also detect repair capability from UNIT_NPC_FLAG_REPAIR (0x1000) on the
vendor NPC entity, so direct vendors without gossip menus also show the
repair button.
2026-04-03 18:18:53 -07:00
Kelsi
8e1addf7a6 fix(rendering): increase ImGui descriptor pool from 100 to 2048
The pool was exhausted by cached spell/item/talent icon textures,
causing vkAllocateDescriptorSets to fail inside ImGui_ImplVulkan_AddTexture.
The NVIDIA driver crashed on the subsequent invalid descriptor write.

Also add a null-check on the returned descriptor set so pool exhaustion
gracefully returns VK_NULL_HANDLE instead of crashing.
2026-04-03 18:14:46 -07:00