From 177409b2d125ade0d9131e9490627b0261abc20d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=A4=A9=E4=BD=BFmisa=EF=BC=81?= Date: Thu, 29 Oct 2026 23:21:43 +0800 Subject: [PATCH] fix(android): work around A850v2 compute texture-size corruption --- README.md | 5 ++ .../0001-tu-a850-compute-txs-resinfo.patch | 33 ++++++++ patches/series.json | 7 ++ tests/manual/a850-compute-texture-size.comp | 17 ++++ tests/manual/a850-compute-texture-size.md | 84 +++++++++++++++++++ 5 files changed, 146 insertions(+) create mode 100644 patches/android/0001-tu-a850-compute-txs-resinfo.patch create mode 100644 tests/manual/a850-compute-texture-size.comp create mode 100644 tests/manual/a850-compute-texture-size.md diff --git a/README.md b/README.md index d609dfa..d0a1f06 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,11 @@ build and the release notes. | `scripts/process_heap_usage.py` | ✓ | ✓ | | `linux/0001` … `linux/0005` (mesh shaders, half-wave subgroups, A8XX cube, bindless and IB fixes) | | ✓ | | `lrz/0001` (A8XX LRZ flush before the next render pass) | ✓ | ✓ | +| `android/0001` (A850v2 compute 2D LOD-zero texture-size workaround) | ✓ | | + +The Android A850v2 texture-size workaround is limited to chip ID `0x44051431`. +See [diagnosis and validation](tests/manual/a850-compute-texture-size.md) for its +scope and a compute probe. A script whose anchor no longer matches Mesa main exits non-zero and a `.patch` that no longer applies fails `git apply`, so upstream drift fails the build instead of shipping a driver without the fix. The `Check` diff --git a/patches/android/0001-tu-a850-compute-txs-resinfo.patch b/patches/android/0001-tu-a850-compute-txs-resinfo.patch new file mode 100644 index 0000000..f5e405d --- /dev/null +++ b/patches/android/0001-tu-a850-compute-txs-resinfo.patch @@ -0,0 +1,33 @@ +--- a/src/freedreno/vulkan/tu_shader.cc ++++ b/src/freedreno/vulkan/tu_shader.cc +@@ -1028,6 +1028,30 @@ + /* for the input attachment case: */ + if (!nir_def_is_intrinsic(bindless)) + tex->src[tex_src_idx].src_type = nir_tex_src_texture_offset; ++ } ++ ++ /* A850v2 GETSIZE can return corrupt compute dimensions. RESINFO uses the ++ * same 2D view width/height at LOD 0, but does not minify for other LODs. ++ */ ++ if (!ref && dev->physical_device->dev_id.chip_id == 0x44051431 && ++ b->shader->info.stage == MESA_SHADER_COMPUTE && ++ tex->op == nir_texop_txs && tex->sampler_dim == GLSL_SAMPLER_DIM_2D && ++ !tex->is_array && tex->def.num_components == 2 && ++ tex->def.bit_size == 32 && tex_src_idx >= 0 && ++ tex->src[tex_src_idx].src_type == nir_tex_src_texture_handle) { ++ int lod_idx = nir_tex_instr_src_index(tex, nir_tex_src_lod); ++ if (lod_idx >= 0 && nir_src_is_const(tex->src[lod_idx].src) && ++ nir_src_as_uint(tex->src[lod_idx].src) == 0) { ++ gl_access_qualifier access = static_cast( ++ (descriptor_valid ? ACCESS_CAN_SPECULATE : 0) | ++ (tex->texture_non_uniform ? ACCESS_NON_UNIFORM : 0)); ++ nir_def *size = nir_bindless_image_size( ++ b, 2, 32, tex->src[tex_src_idx].src.ssa, nir_imm_int(b, 0), ++ .image_dim = GLSL_SAMPLER_DIM_2D, .image_array = false, ++ .access = access); ++ nir_def_replace(&tex->def, size); ++ return true; ++ } + } + + if (dev->physical_device->compiler_options.enable_texel_buffer_emulation && diff --git a/patches/series.json b/patches/series.json index 4047232..1cbfb8b 100644 --- a/patches/series.json +++ b/patches/series.json @@ -156,5 +156,12 @@ "linux" ], "summary": "Reset the LRZ controls after the end-of-pass LRZ flush on A8XX, as freedreno does, so a large fast-cleared LRZ buffer no longer hangs the GPU" + }, + { + "file": "android/0001-tu-a850-compute-txs-resinfo.patch", + "targets": [ + "android" + ], + "summary": "Use RESINFO for A850v2 compute 2D LOD-zero texture sizes to fix black or fragmented LSFG frames" } ] diff --git a/tests/manual/a850-compute-texture-size.comp b/tests/manual/a850-compute-texture-size.comp new file mode 100644 index 0000000..1e102f5 --- /dev/null +++ b/tests/manual/a850-compute-texture-size.comp @@ -0,0 +1,17 @@ +#version 450 +layout(local_size_x=16,local_size_y=16,local_size_z=1) in; +layout(set=0,binding=1) uniform sampler s; +layout(set=0,binding=3) uniform texture2D inputFrame; +layout(set=0,binding=8,rgba8) writeonly uniform image2D outputFrame; +void main() { + ivec2 p=ivec2(gl_GlobalInvocationID.xy); + if(any(greaterThanEqual(p,ivec2(1562,720)))) return; + ivec2 q=p.y<360?textureSize(sampler2D(inputFrame,s),0):imageSize(outputFrame); + // Leave the partial final column blue; shifting a uint by 32 is undefined. + uint bit=uint(p.x/48); + if(bit>=32u) { + imageStore(outputFrame,p,vec4(0,0,1,1)); + return; + } + imageStore(outputFrame,p,vec4(float((uint(q.x)>>bit)&1),float((uint(q.y)>>bit)&1),1,1)); +} diff --git a/tests/manual/a850-compute-texture-size.md b/tests/manual/a850-compute-texture-size.md new file mode 100644 index 0000000..7b92881 --- /dev/null +++ b/tests/manual/a850-compute-texture-size.md @@ -0,0 +1,84 @@ +# A850v2 compute texture-size corruption + +With DD-Turnip v0.3.4, DroidDeck's original LSFG 2x shaders produced alternating +normal and black frames, sometimes with fragments of the image in the top-left +corner. The workaround replaces a narrowly scoped compute texture-size query +with the existing bindless image-size lowering. + +## Isolation + +In the affected compute path, copying with integer-coordinate `texelFetch` +worked. Sampling with `textureLod` using coordinates divided by +`textureSize(input, 0)` reproduced the corruption. Replacing the queried size +with the known dimensions (1562 x 720) made that sampling path work. + +A diagnostic encoding the integer dimensions into color bits showed that +`textureSize` returned incorrect, workgroup-dependent values, whereas +`imageSize` returned the expected dimensions. This implicates the GETSIZE query +path; it is not just a floating-point reciprocal or LSFG precision issue. +The underlying hardware/driver cause of the incorrect GETSIZE result is not +established by this test. + +## Workaround scope + +The Android patch applies only to chip ID `0x44051431` (A850v2), compute shaders, +non-array 2D textures, two 32-bit result components, an already lowered bindless +texture handle, and a constant LOD of zero. It preserves descriptor validity +and non-uniform access flags. + +For this case, RESINFO reads the width and height already encoded for the +view's base mip. It does not perform GETSIZE's LOD minification, so the rewrite +must not be extended to nonzero or dynamic LODs. Array, cube, 3D, multisample +queries, other shader stages, and other chip IDs keep their original path. +The recipe enables this only for Android, the platform tested here. + +## Manual diagnostic + +[`a850-compute-texture-size.comp`](a850-compute-texture-size.comp) is the small +GLSL probe used during diagnosis, with a guard added to clear the partial +final column instead of shifting by 32. It is a shader, not a standalone Vulkan +test runner. To run it in a compute harness: + +1. Create a 1562 x 720 sampled 2D image and an equally sized RGBA8 storage image, + each with a non-array 2D view. Bind a sampler at set 0/binding 1, the sampled + image at binding 3, and the output storage image at binding 8. +2. Compile as a Vulkan compute shader, for example with + `glslangValidator -V a850-compute-texture-size.comp -o query.spv`. +3. Transition the images appropriately and dispatch 98 x 45 x 1 workgroups + (local size 16 x 16). Make compute storage writes visible before reading + back or displaying the output. +4. Each 48-pixel column encodes one bit, least significant bit first. Red + represents width, green height, and blue is always one. The top half uses + `textureSize`, the bottom half `imageSize`. Both halves should encode + width 1562 (bits 1, 3, 4, 9, 10) and height 720 (bits 4, 6, 7, 9). + Pixels at x >= 1536 are blue padding. + +The observed failure and success below were obtained in DroidDeck's compute +pipeline with these bindings. Reproduction in a separate harness has not +been verified; pipeline and descriptor state may matter. + +## Recorded validation + +- Device: Xiaomi M154FF, SM8975, Adreno 850v2 (`0x44051431`), Android 17/API 37. +- Application: DroidDeck 0.6.0 (`e9e0186`, runtime r9); original LSFG FP16, 2x. +- Baseline: DD-Turnip v0.3.4, Mesa + `e2ab6687d5ad700cc2b856af684aa014f9dae943`, recipe + `de8bba431f1c8dd96b61c403262cf74f7756ef19`. +- Build: official Android patch set plus this workaround, NDK r26d, + Android API 34 target, platform SDK 36. No experimental descriptor-cache + patch was included. +- Before: diagnostic top half had incorrect dimensions; bottom half correctly + encoded 1562 x 720. After: both halves consistently encoded 1562 x 720. +- Restored the original LSFG shaders and actual 2x generation after diagnosis. + In the subsequent 32-frame capture, the prior black corruption was absent; + the device user also confirmed normal rendering. +- Tested library SHA-256: + `c5475d61a53063c18b7eddcd535a1a2f191081269264d4578af1957a74febc4c`. + The submitted patch adds an explanatory comment to the tested code; its + executable logic is identical. + +This is targeted application/device validation, not Vulkan CTS coverage. +Other GPUs, Linux, null descriptors, and nonzero view base mips have not been +tested on hardware. A separate application setting (adaptive target 60 FPS) +improved perceived input latency; that is not a driver performance result or +part of this patch.