Skip to content

fix(metal): mirror dual-use vit.norm weights to device - #7

Merged
richiejp merged 1 commit into
localai-org:masterfrom
localai-org-maint-bot:cron/pr4-without-generated-assets
Aug 12, 2026
Merged

fix(metal): mirror dual-use vit.norm weights to device#7
richiejp merged 1 commit into
localai-org:masterfrom
localai-org-maint-bot:cron/pr4-without-generated-assets

Conversation

@localai-org-maint-bot

Copy link
Copy Markdown

Supersedes #4 at @mudler's request, preserving @Jinshin2534's original fix while omitting the generated depth.pfm and depth.png artifacts.

The branch is based on current master and contains only changes to:

  • src/dino_backbone.cpp
  • src/model_loader.cpp
  • src/model_loader.hpp

Verification:

  • git diff --check origin/master...HEAD
  • exact changed-file allow-list confirming neither generated artifact is present

A focused CMake build could not be run on this cron host because cmake is not installed.

The fused depth path (Engine::depth_native_fused -> DinoBackbone::
build_feats_graph) applies the ViT final layernorm as ggml graph ops
(ggml_extend.hpp `layernorm`: ggml_norm -> ggml_mul(vit.norm.weight) ->
ggml_add(vit.norm.bias)). But offload_weights classified vit.norm.weight
and vit.norm.bias as host-read tensors and kept them host-only, so on a
GPU backend these graph leaves had no device buffer (t->buffer == NULL).

On CPU this was harmless (the CPU backend reads t->data directly). On
Metal it segfaults: ggml_metal_get_buffer_id() guards t == NULL but not
buffer == NULL, so encoding the norm's MUL/ADD operand dereferences
buffer->context at NULL+0x60 (EXC_BAD_ACCESS). Reproduced on Apple M4 /
macOS 26.6 with depth-anything-base-q4_k.gguf; GGML_METAL_FUSION_DISABLE=1
only moved the crash from ggml_metal_op_norm to ggml_metal_op_bin.

These two weights are genuinely dual-use: graph operands in the fused
path AND host `->data` reads in the unfused / multi-view path
(layernorm_host). Mirror them onto the device like every other weight so
the graph has a valid buffer, and preserve their host originals in a new
host_tensors_ map exposed via ModelLoader::host_tensor(), which the two
layernorm_host sites now use. vit.pos_embed / vit.camera_token stay
host-only (they only ever feed host-computed graph inputs).

Verified on depth-anything-base-q4_k.gguf:
- CPU output bit-identical (min=0.7395 max=2.4074, unchanged).
- Metal no longer crashes and produces a correct depth map.

Fixes localai-org#3.

Assisted-by: Claude:opus-4.8
@localai-org-maint-bot
localai-org-maint-bot force-pushed the cron/pr4-without-generated-assets branch from ba63157 to 7ac14bb Compare August 12, 2026 09:04
@richiejp
richiejp merged commit 54abd5c into localai-org:master Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants