@@ -190,8 +190,9 @@ bool use_gqa_av_coop(
190190}
191191
192192// Resolve whether the AV decode path uses the GQA-reuse coop shader, honoring
193- // the test-only shader_override knob (see SDPA.h): -1/kDummyValueRef
194- // auto-selects via use_gqa_av_coop; 0 forces off; 1 forces on.
193+ // the test-only shader_override knob (see SDPA.h): auto selects via
194+ // use_gqa_av_coop; kShaderOverrideForceNonGqa forces off; any other value
195+ // forces the GQA family on.
195196bool resolve_use_gqa (
196197 ComputeGraph* graph,
197198 const ValueRef shader_override,
@@ -200,20 +201,38 @@ bool resolve_use_gqa(
200201 if (shader_override == kDummyValueRef ) {
201202 return use_gqa_av_coop (graph, num_q_heads, num_kv_heads);
202203 }
203- const bool force_gqa = graph->extract_scalar <int64_t >(shader_override) != 0 ;
204+ const bool force_gqa = graph->extract_scalar <int64_t >(shader_override) !=
205+ kShaderOverrideForceNonGqa ;
204206 // Forcing the GQA shader on an ineligible shape would silently drop query
205207 // heads (z-dispatch = Hkv cannot cover Hq, or group size exceeds the shader's
206208 // fixed accumulator array). Fail loudly instead of producing garbage output.
207209 if (force_gqa) {
208210 VK_CHECK_COND (
209211 num_kv_heads > 0 && num_q_heads % num_kv_heads == 0 &&
210212 num_q_heads / num_kv_heads <= kMaxGqaGroupSize ,
211- " shader_override=1 requires a GQA-eligible shape: Hq divisible by Hkv and "
212- " group size <= kMaxGqaGroupSize" );
213+ " forcing GQA via shader_override requires a GQA-eligible shape: Hq "
214+ " divisible by Hkv and group size <= kMaxGqaGroupSize" );
213215 }
214216 return force_gqa;
215217}
216218
219+ // Resolve whether the GQA-reuse AV path uses the head_dim output-tiled (_tile2)
220+ // variant. Auto (no override) and kShaderOverrideForceGqa defer to the vendor
221+ // gate (tile2 on Adreno); the force values pin the variant on any device (see
222+ // SDPA.h), giving the Adreno-only tile2 variant deterministic test coverage.
223+ bool resolve_use_tile2 (ComputeGraph* graph, const ValueRef shader_override) {
224+ if (shader_override != kDummyValueRef ) {
225+ const int64_t ov = graph->extract_scalar <int64_t >(shader_override);
226+ if (ov == kShaderOverrideForceTile2 ) {
227+ return true ;
228+ }
229+ if (ov == kShaderOverrideForceBase ) {
230+ return false ;
231+ }
232+ }
233+ return graph->device_is_adreno ();
234+ }
235+
217236vkapi::ShaderInfo pick_sdpa_qk_shader (
218237 ComputeGraph* graph,
219238 const std::vector<ArgGroup>& args,
@@ -347,6 +366,16 @@ vkapi::ShaderInfo pick_sdpa_av_shader(
347366 // reusing it across the group. Cuts the dominant V-cache traffic ~Gx
348367 // for this bandwidth-bound kernel.
349368 shader_name += " _gqa_coop" ;
369+ // The _tile2 (head_dim output-tiled) variant amortizes the attn-weight
370+ // loads and shared-memory reduction over more outputs per workgroup. A
371+ // consistent win on Adreno (AV ~1.14-1.63x) but a regression on Mali at
372+ // common decode contexts (~0.67-0.86x, interleaved median), so it is
373+ // selected vendor-adaptively (tests can pin it via shader_override; see
374+ // resolve_use_tile2). pick_sdpa_av_global_wg_size keys the x-dim
375+ // collapse off this same _tile2 suffix.
376+ if (resolve_use_tile2 (graph, shader_override)) {
377+ shader_name += " _tile2" ;
378+ }
350379 } else {
351380 shader_name += " _coop" ;
352381 }
@@ -389,7 +418,14 @@ utils::uvec3 pick_sdpa_av_global_wg_size(
389418 if (shader.kernel_name .find (" _gqa_coop" ) != std::string::npos) {
390419 const ValueRef v = args.at (1 ).refs .at (1 );
391420 const uint32_t num_kv_heads = graph->size_at <uint32_t >(-2 , v);
392- return {N4 , M4 , static_cast <uint32_t >(num_kv_heads * d.B )};
421+ // The _tile2 variant has each workgroup own 2 head_dim texels, so its x-dim
422+ // collapses to div_up(D4, 2). The plain (TILE_N4 == 1) variant keeps x ==
423+ // D4.
424+ const uint32_t x_dim =
425+ shader.kernel_name .find (" _tile2" ) != std::string::npos
426+ ? utils::div_up (N4 , 2u )
427+ : N4 ;
428+ return {x_dim, M4 , static_cast <uint32_t >(num_kv_heads * d.B )};
393429 }
394430
395431 return {N4 , M4 , static_cast <uint32_t >(d.H * d.B )};
0 commit comments