Steps to reproduce
-
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"
-
From Dart, bind the block by name: frag.getUniformSlot('MyParams').
-
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.
Steps to reproduce
Write a Flutter GPU fragment shader whose uniform block instance name does not normalize to the block name (e.g. block
MyParams, instanceparams):From Dart, bind the block by name:
frag.getUniformSlot('MyParams').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
-1and 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 100has no UBOs, so SPIRV-Cross lowersuniform MyParams { ... } params;to a flat struct + uniforms prefixed by the instance name (params.base_color).impeller/renderer/backend/gles/buffer_bindings_gles.ccrecords locations under the instance name but looks them up under the block name.NormalizeUniformKeystrips underscores and uppercases, so engine shaders that happen to follow theFrameInfo/frame_infoconvention 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 targetingkOpenGLES/kRuntimeStageGLES*, useCompilerGLSL::set_nameto rename each uniform block instance variable to a deterministic form whoseNormalizeUniformKeyequals 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
BufferBindingsGLESon it; that requires aShaderBundleFormatVersionbump.Code sample
Code sample
A runnable repro is the "Toon" entry in
flutter_scene'sexamples/flutter_app(flutter_scene0.13.0 introducedShaderMaterial, 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, enginea8bf5254392e. Expected on any device using the GLES Impeller backend.