Skip to content

[Impeller] Metal backend crashes when binding a texture whose sampler was dead-code-eliminated #190034

Closed

Description

@bdero

Steps to reproduce

  1. Write a Flutter GPU fragment shader that declares a sampler2D used only in code the optimizer removes, so SPIRV-Cross keeps it in the reflected resource list but drops it from the emitted Metal shader.
  2. From Dart, bind a texture to that sampler with renderPass.bindTexture(shader.getUniformSlot('the_sampler'), texture).
  3. Draw on Metal with flutter run -d macos --enable-flutter-gpu --enable-impeller.

Expected results

Binding a texture for a sampler the compiled shader does not use should be a harmless no-op, the way the OpenGLES and Vulkan backends already handle it. It should never crash the driver.

Actual results

The Metal backend passes the dead-code sentinel index uint32_t(-1) (4294967295) straight to setFragmentTexture:atIndex:, which has no range check, so the AGX driver crashes.

0  AGXMetalG15X  -[AGXG15XFamilyRenderContext setFragmentTexture:atIndex:]
1  FlutterMacOS  impeller::PassBindingsCacheMTL::SetTexture(impeller::ShaderStage, unsigned long long, id<MTLTexture>)
2  FlutterMacOS  impeller::RenderPassMTL::BindDynamicResource(impeller::ShaderStage, impeller::DescriptorType, impeller::SampledImageSlot const&, ...)

Code sample

uniform sampler2D tex_a;
uniform sampler2D tex_b;   // reflected, but dropped from the Metal shader
in vec2 v_uv;
out vec4 frag_color;
void main() {
  vec4 c = texture(tex_a, v_uv);
  if (false) { c += texture(tex_b, v_uv); }   // makes tex_b dead
  frag_color = c;
}

Then bind both tex_a and tex_b from Dart and draw. Binding tex_b crashes. (This shader shape needs a quick confirmation that it lands on the reflected-but-dropped path, see the note below.)

Root cause

impellerc still lists a dead sampler in its reflection but stamps that entry's index with uint32_t(-1), because get_automatic_msl_resource_binding returns ~0u for a sampler SPIRV-Cross removed from the emitted Metal. That value is written to the shader bundle, cached on the Dart Shader, and forwarded through RenderPassMTL::BindDynamicResource to setFragmentTexture:atIndex: with no range check. Every other backend avoids the problem. Vulkan binds by the SPIR-V binding decoration, and OpenGLES binds by name and skips a location of -1 (buffer_bindings_gles.cc, "The texture binding was optimized out of the shader. Continue.").

Proposed fix

Any one of these stops the crash, and they compose. None of them add per-draw work.

  • Skip a texture binding whose ext_res_0 is uint32_t(-1) when loading the shader in lib/gpu/shader_library.cc, so the bind becomes a no-op like OpenGLES. This works with already-built shader bundles.
  • Stop emitting sampled_images entries with a sentinel index in reflector.cc, so the bundle reflection matches the compiled shader.
  • As a backstop, skip a bind whose index is the sentinel or out of range in pass_bindings_cache_mtl, so no invalid binding can ever reach the driver.

Activity

  1. added
    c: crashStack traces logged to the console
    engineflutter/engine related. See also e: labels.
    platform-macosBuilding on or for macOS specifically
    e: impellerImpeller rendering backend issues and features requests
    on Jul 26, 2026
  2. moved this to 🤔 Needs Triage in Flutter GPUon Jul 26, 2026
  3. moved this from 🤔 Needs Triage to 🐞 Bugs in Flutter GPUon Jul 26, 2026
  4. moved this from 🐞 Bugs to ✅ Done in Flutter GPUon Aug 2, 2026
  5. github-actions commented on Aug 16, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  6. locked as resolved and limited conversation to collaborators on Aug 16, 2026
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

    c: crashStack traces logged to the consolee: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.flutter-gpuplatform-macosBuilding on or for macOS specificallyteam-fluttergpuOwned by Flutter GPU team

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions