Skip to content

[Intel XPU] Qwen3-VL DeepStack multi-image boolean indexing selects wrong token count (1059 vs 2061); CPU-index workaround #16991

Open

Description

@Goldlionren

Summary

Qwen-Image 2.1 / Qwen3-VL two-reference-image conditioning fails on Intel XPU in ComfyUI 0.39.0 because PyTorch 2.14.1+xpu silently returns the wrong number of entries for a boolean-mask nonzero / advanced index. Single-image conditioning succeeds.

Root cause tracked upstream: pytorch/pytorch#200713 (standalone reproducible, not specific to ComfyUI).

Reproduction in ComfyUI

Environment:

  • Ubuntu 26.04, Intel Core Ultra 9 288V / Arc 140V (Lunar Lake Xe2; 32 GB unified RAM)
  • ComfyUI 0.39.0, Python 3.13.16, torch 2.14.1+xpu
  • Qwen-Image 2.1 TextEncode with image_1 + image_2
  • Qwen3-VL 8B FP8 Heretic text encoder; same issue with FP16 runtime mode
  • --enable-triton-backend --use-pytorch-cross-attention; Triton is not involved in standalone PyTorch repro.

With a temporary diagnostic at build_image_inputs, the two image spans are correct and nonoverlapping:

seq=2133
refs=[(22,1025,[1025,1025,1025]), (1053,1036,[1036,1036,1036])]
mask_tokens=2061
deepstack_tokens=[2061,2061,2061]

But the actual DeepStack injection in comfy/text_encoders/llama.py:

x[visual_pos_masks] = x[visual_pos_masks] + deepstack_embeds[i].to(x)

raises:

RuntimeError: The size of tensor a (1059) must match the size of tensor b (2061) at non-singleton dimension 0

Standalone PyTorch proof: mask.sum()==2061, but XPU mask.nonzero().numel()==1059 and x[mask].shape[0]==1059; CPU yields 2061. See pytorch/pytorch#200713 for the compact repro.

Verified local workaround (Qwen3-VL, batch size 1)

Avoid XPU boolean indexing on this path only. Calculate integer indices from a CPU copy of the mask, then do XPU integer selection/writeback. A local change limited to x.device.type == "xpu", with shape validation and unchanged non-XPU branch, was confirmed by the reporter to allow the previously failing two-image workflow to work:

if x.device.type == "xpu":
    if x.ndim != 3 or x.shape[0] != 1 or visual_pos_masks.shape != x.shape[:2]:
        raise RuntimeError("Unexpected DeepStack shape for XPU workaround")
    indices_cpu = torch.nonzero(visual_pos_masks[0].cpu(), as_tuple=True)[0]
    features = deepstack_embeds[i].to(x)
    if tuple(features.shape) != (indices_cpu.numel(), x.shape[-1]):
        raise RuntimeError("DeepStack feature/index mismatch")
    indices = indices_cpu.to(x.device)
    updated = x.index_select(1, indices) + features.unsqueeze(0)
    x.index_copy_(1, indices, updated)
else:
    x[visual_pos_masks] = x[visual_pos_masks] + deepstack_embeds[i].to(x)

This is a proposed compatibility workaround, not a PyTorch fix or a claim of universal support (other shapes, devices and XPU drivers remain untested). A future upstream solution may instead correct the XPU kernel itself.

Request

Could ComfyUI consider a narrow XPU-only workaround (and regression test) for this multi-reference DeepStack path until PyTorch fixes the indexing bug? In the meantime, linking this to pytorch/pytorch#200713 should help users distinguish it from an OOM or image-resolution issue.

Thanks.

Activity

  1. Goldlionren commented on Oct 13, 2026

    @Goldlionren
    Author

    Follow-up: visual-output validation not yet passed

    Important correction to the original report: after applying the XPU integer-index workaround, the two-reference workflow no longer raises the 1059 vs 2061 exception and can generate a PNG; however, the reporter has now inspected the generated image and it is visually corrupted (large checkerboard-like region over the upper ~2/3 of a 1440×896 output, with only the lower region containing coherent image content).

    The PNG's alpha channel is nearly opaque (observed alpha range 250–255), so the checkerboard is embedded RGB content, not viewer transparency. This does not yet prove the workaround caused the corruption. Potential variables still to isolate: input reference images, output/compositing nodes, FP8 text-encoder mode, CK Triton/quantized model paths, and other XPU indexing errors. No clean two-image quality pass should be claimed yet.

    Revised status: structural DeepStack shape mismatch bypassed; multi-image output correctness UNVERIFIED/FAIL for current example. We will compare direct VAE-decoded SaveImage output, single-vs-dual image runs, and reference-image contents before identifying the cause. The upstream PyTorch bug and standalone reproducer remain independently confirmed.

  2. Goldlionren commented on Oct 13, 2026

    @Goldlionren
    Author

    Further A/B isolation on the reporter's Arc 140V: restoring stock comfy/text_encoders/llama.py immediately restores correct single-image generation. The earlier XPU branch using index_select(1, indices) plus index_copy_(1, indices, updated) caused severe checkerboard-like image corruption in both single- and two-image workflows, even though a small standalone indexing-mapping test produced exact CPU equality. Toggling CK Triton or FP8 text-encoder mode does not affect the correct single-image output after restoring stock code. Please do not adopt the earlier index_copy_ workaround as a verified image-quality fix. We are testing a more conservative approach: keep the original boolean-index assignment for the working single contiguous image range; for multiple separated image ranges, derive contiguous spans on CPU and write each range with ordinary slice assignment, avoiding both XPU boolean nonzero and index_copy_. End-to-end image quality is not yet verified for this new approach.

  3. Goldlionren commented on Oct 13, 2026

    @Goldlionren
    Author

    Update: V2 contiguous-slice workaround passes single-image regression and full dual-image inference (visual quality still to be confirmed)

    The earlier XPU index_select + index_copy_ workaround was rejected after corrupted single-image outputs. The reporter restored stock llama.py: single-image output was correct, regardless of FP8 text-encoder toggle or CK Triton toggle.

    A second workaround now preserves the exact stock boolean-assignment for a single contiguous image span and, only when more than one image span is present, constructs image spans from a CPU copy of the boolean mask and uses basic PyTorch slice assignment rather than XPU boolean advanced indexing or index_copy_.

    Relevant multi-image section of the working local experimental patch (JR-XPU-DEEPSTACK-SLICE-V2):

    # In the DeepStack injection block; assume x.shape == [1, seq, hidden],
    # and validated visual_pos_masks.shape == [1, seq].
    mask_cpu = visual_pos_masks[0].detach().to(device="cpu", dtype=torch.int8)
    padded = torch.cat((mask_cpu.new_zeros(1), mask_cpu, mask_cpu.new_zeros(1)))
    edges = torch.diff(padded)
    starts = (edges == 1).nonzero(as_tuple=True)[0].tolist()
    ends = (edges == -1).nonzero(as_tuple=True)[0].tolist()
    
    if len(starts) <= 1:
        x[visual_pos_masks] = x[visual_pos_masks] + deepstack_embeds[i].to(x)
    else:
        # Validate x/mask shapes, len(starts)==len(ends), 
        # and features.shape == [sum(end-start), x.shape[-1]].
        features = deepstack_embeds[i].to(x)
        cursor = 0
        for start, end in zip(starts, ends):
            count = end - start
            x[:, start:end, :] = (
                x[:, start:end, :]
                + features[cursor:cursor + count].unsqueeze(0)
            )
            cursor += count

    The reporter ran a standalone slice correctness check without errors, then ran real Qwen-Image 2.1 FP8 text-encoder + INT8 ConvRot diffusion workflows:

    • Single image (stock path): seq=1069, image tokens 1025; generated visually correct output; 25/25 steps; 298.56 sec total.
    • Two images (V2 path): seq=2135, image spans [22,1047), [1053,2089), 2061 tokens. The V2 diagnostic printed for both encode passes. Completed 25/25 sampling steps and image generation, 368.98 sec total; no shape exception or crash.
    • Two images at a higher resolution with CK Triton enabled: seq=2136, same image spans; completed 25/25, 500.94 sec total. These times must NOT be used to infer Triton impact because output resolution changed.
    • The reporter has not yet provided side-by-side visual output validation for V2 dual-image outputs. Please treat V2 as an experimental workaround pending two-image image-quality verification, not as a production-ready fix.

    Standalone PyTorch boolean indexing bug: pytorch/pytorch#200713.

  4. Goldlionren commented on Oct 13, 2026

    @Goldlionren
    Author

    Final visual validation update — V2 Slice workaround PASSED on Arc 140V

    The reporter has now inspected the actual generated PNG outputs and confirms both the single-image and dual-image outputs are visually correct, with none of the earlier V1 checkerboard/vertical-layout artifacts. This validates the V2 CPU-span + XPU basic slice assignment workaround on the reported Ubuntu 26.04 / Arc 140V / torch 2.14.1+xpu environment (but not other hardware yet).

    Evidence already provided in the previous comment: single image 25/25 steps and 298.56 s; dual image 25/25 steps and 368.98 s, with spans [22,1047), [1053,2089) and 2061 DeepStack tokens. Another higher-resolution dual-image 25/25 pass with CK Triton enabled took 500.94 s; this is not a controlled Triton performance comparison.

    V1 index_select/index_copy_ must not be used due to severe image quality regression. V2 preserves original single-span update, using basic per-span XPU slices only for two or more disjoint image spans. A single-file patch and reproducible rollback/upgrade tooling have been prepared. A PR is pending repository-contents write authorization for the reporter's fork (the current GitHub integration returns HTTP 403 on branch creation); no PR is claimed until a push succeeds.

    Root-cause tracker: pytorch/pytorch#200713.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions