Skip to content

[Impeller] OpenGL ES backend silently fails to bind uniform block members when the GLSL instance name does not match the block name #186393

Description

@bdero

Steps to reproduce

  1. Write a Flutter GPU fragment shader whose uniform block instance name does not normalize to the block name (e.g. block MyParams, instance params):

    uniform MyParams {
      vec4 base_color;
    } params;   // instance name "params", not "my_params"
  2. From Dart, bind the block by name: frag.getUniformSlot('MyParams').

  3. Run on the OpenGL ES Impeller backend.

Expected results

Block members bind and render correctly, matching Vulkan and Metal.

Actual results

Every member of the block resolves to GL location -1 and is silently skipped (no warning is logged, even with --verbose-system-logs), so the shader reads zero everywhere. Vulkan and Metal are unaffected (they bind by descriptor index, not name).

Root cause

#version 100 has no UBOs, so SPIRV-Cross lowers uniform MyParams { ... } params; to a flat struct + uniforms prefixed by the instance name (params.base_color). impeller/renderer/backend/gles/buffer_bindings_gles.cc records locations under the instance name but looks them up under the block name. NormalizeUniformKey strips underscores and uppercases, so engine shaders that happen to follow the FrameInfo / frame_info convention match; user shaders that don't (MyParams / params) don't.

FragmentProgram / RuntimeEffect is not affected: its GLES path binds per individual uniform, not per block.

Possible fix

In impeller/compiler/compiler.cc, when targeting kOpenGLES / kRuntimeStageGLES*, use CompilerGLSL::set_name to rename each uniform block instance variable to a deterministic form whose NormalizeUniformKey equals the block name's (e.g. lowercased block name). Compiler-only change, no shader-bundle-format bump, no behavior change for existing engine shaders.

A longer-term alternative is to carry the instance name through reflection metadata and key BufferBindingsGLES on it; that requires a ShaderBundleFormatVersion bump.

Code sample

Code sample
import 'package:flutter_gpu/gpu.dart' as gpu;

final lib = gpu.ShaderLibrary.fromAsset('build/shaderbundles/my.shaderbundle')!;
final pipeline = gpu.gpuContext.createRenderPipeline(vertexShader, lib['MyFragment']!);
pass.bindUniform(
  lib['MyFragment']!.getUniformSlot('MyParams'),  // looked up by BLOCK name
  transients.emplace(myParamsBytes),
);
pass.draw();
// Renders on Vulkan/Metal, draws nothing on the OpenGL ES backend.

A runnable repro is the "Toon" entry in flutter_scene's examples/flutter_app (flutter_scene 0.13.0 introduced ShaderMaterial, which is the first user of this path).

Flutter Doctor output

Doctor output

Reproduced on Pixel 10 Pro (PowerVR D-Series DXT-48-1536, OpenGL ES 3.2) on framework 85d99ba309, engine a8bf5254392e. Expected on any device using the GLES Impeller backend.

Metadata

Metadata

Assignees

Labels

e: impellerImpeller rendering backend issues and features requestse: openglengineflutter/engine related. See also e: labels.flutter-gputeam-fluttergpuOwned by Flutter GPU team

Type

No type
No fields configured for issues without a type.

Projects

Status
✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions