PowerVR DXT-48-1536: incorrect results from k-quant Vulkan compute shaders in llama.cpp

GPU: PowerVR D-Series DXT-48-1536 MC1 (Google Tensor G5)
Driver: Vulkan 1.4.317, driverVersion 1.662.3024, VK_DRIVER_ID_IMAGINATION_PROPRIETARY
OS: Android 17 (API 37, Pixel 10 Pro), Bionic/Termux


In llama.cpp (b10055, commit e8f19cc0a), all k-quant compute shaders (Q2_K through Q6_K) produce
numerically incorrect results on this GPU. Non-k-quant types (Q4_0, Q5_0, Q8_0,
F16, F32) work correctly.

Affected shaders:

  • mul_mat_vec_q{2,3,4,5,6}_k_f32_f32 and their _f16_f32 variants
  • matmul_q{2,3,4,5,6}_k_f32 and their _f16acc variants
  • dequant_q{2,3,4,5,6}_k (in standalone form, likely affected as well)

Reproduction:

# Working:
./llama-simple -m model-q4_0.gguf -p "!!!!!!!!" -n 16
-> "!!!!!!!! !!!!!!!!" (correct)

# Broken:
./llama-simple -m model-q4_k_m.gguf -p "!!!!!!!!" -n 16
-> "!!iday !!(day !!(day" (garbled)

The error is deterministic (same garbled output every run), not random.

Additional observations:

  1. Pipeline creation initially crashes with SHADER_REDUCTION_MODE_SUBGROUP
    for NUM_COLS >= 2. Changing to SHADER_REDUCTION_MODE_HYBRID avoids the
    crash but the output is still wrong (PR #20565 in llama.cpp).

  2. subgroupSize = 128. The k-quant shaders force a workgroup targeting 16-thread
    subgroups (use_subgroups16), while non-k-quant shaders use the full 128.
    VK_EXT_subgroup_size_control is not exposed by the driver, so it is unclear
    if the effective subgroup size is valid for reduced-size subgroup operations.

  3. Setting GGML_VK_DISABLE_F16=1 does not fix the issue — it only switches
    between f16-accumulator and f32-accumulator variants of the same buggy
    shader. Integer dot product (VK_KHR_shader_integer_dot_product) is not
    supported on this device (int dot: 0), so the q8_1 code path is not used.

Suspected root cause: The k-quant block format uses complex hierarchical
bitfield packing — multiple 4-6 bit scale factors interleaved across uint32 words.
The working types (Q4_0 etc.) use simple contiguous 32-value blocks with one
scale factor. The driver’s shader compiler may have an optimization pass that
produces incorrect code for this more complex bit-extraction pattern.

Has anyone else seen this on PowerVR DXT or other recent PowerVR GPUs?
Is there a known driver bug with uint32 bitfield extraction in compute shaders?