Skip to content

[Google3 Bug]: [web] FragmentShader.getImageSampler throws UnsupportedError on CanvasKit and Skwasm #194290

Closed

Description

@Dipakkumarjena

Steps to reproduce

  1. Create an app with flutter create bug.
  2. Add the shader from the code sample as shaders/sampler.frag and register it in pubspec.yaml (see code sample).
  3. Replace lib/main.dart with the code sample.
  4. Run it on web with either renderer:
    • CanvasKit: flutter run -d chrome
    • Skwasm: flutter run -d chrome --wasm
  5. Observe the exception thrown from SamplerPainter.paint.

Expected results

shader.getImageSampler('uTexture') returns an ImageSamplerSlot, and slot.set(image) binds the image to the uTexture sampler, equivalent to shader.setImageSampler(0, image). The 200x200 square renders the image (left half red, right half blue).

This is how the API behaves on mobile and desktop, where FragmentShader.getImageSampler is implemented in lib/ui/painting.dart (added in #176749).

Actual results

On both web renderers, getImageSampler throws immediately:

Unsupported operation: getImageSampler is not supported on the web.

Both web backends stub the method out:

Every other uniform type can already be bound by name on web (getUniformFloat, getUniformVec2, ..., getUniformMat4Array), and ShaderData.fromBytes already parses SampledImage uniforms. Samplers are the only uniform type that can't be bound by name on web. So cross-platform code that uses the by-name API has to special-case web and fall back to index-based setImageSampler.

Proposed fix (I have a patch and will send a PR referencing this issue)

  1. Add _getImageSamplerIndex(String name) to CkFragmentProgram and SkwasmFragmentProgram, mirroring FragmentProgram._getImageSamplerIndex. The index is the number of SampledImage uniforms declared before name. It throws ArgumentError if name doesn't exist or isn't a sampler.
  2. Return an ImageSamplerSlot from CkFragmentShader.getImageSampler and SkwasmFragmentShader.getImageSampler instead of throwing.
  3. In EngineFragmentShader.getImageSampler, wrap the backend slot so that set() goes through EngineFragmentShader.setImageSampler. That keeps the BackendImageShader it creates attached to the shader finalizer and tracked in samplerImages, the same as the index-based path.
  4. Add coverage to lib/web_ui/test/ui/fragment_shader_test.dart. It has no getImageSampler tests today.
Related: Skwasm _getUniformArray allocates floatCount slots per element (fixed in the same PR)

SkwasmFragmentShader._getUniformArray creates info.floatCount slots (the size of the whole array) for each element. Only the first elementSize slots are used. For uniform vec4 uData[64] that is 64 × 256 = 16,384 slot objects instead of 256. The slots it does use also report index as the offset within the element (j), while the CanvasKit implementation reports the offset within the uniform (i * elementSize + j). Values are still written to the correct offsets, so the effect is wasted allocations and inconsistent index values, not wrong rendering. The fix makes Skwasm match CanvasKit.

Code sample

Code sample

shaders/sampler.frag:

#version 460 core

#include <flutter/runtime_effect.glsl>

uniform vec2 uSize;
uniform sampler2D uTexture;

out vec4 fragColor;

void main() {
  vec2 uv = FlutterFragCoord().xy / uSize;
  fragColor = texture(uTexture, uv);
}

pubspec.yaml (excerpt):

flutter:
  shaders:
    - shaders/sampler.frag

lib/main.dart:

import 'dart:ui' as ui;

import 'package:flutter/material.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final ui.FragmentProgram program = await ui.FragmentProgram.fromAsset(
    'shaders/sampler.frag',
  );
  final ui.Image image = await _createTestImage();
  runApp(SamplerApp(shader: program.fragmentShader(), image: image));
}

/// Creates a 100x100 image: left half red, right half blue.
Future<ui.Image> _createTestImage() {
  final recorder = ui.PictureRecorder();
  final canvas = Canvas(recorder);
  canvas.drawRect(
    const Rect.fromLTWH(0, 0, 50, 100),
    Paint()..color = const Color(0xFFFF0000),
  );
  canvas.drawRect(
    const Rect.fromLTWH(50, 0, 50, 100),
    Paint()..color = const Color(0xFF0000FF),
  );
  return recorder.endRecording().toImage(100, 100);
}

class SamplerApp extends StatelessWidget {
  const SamplerApp({super.key, required this.shader, required this.image});

  final ui.FragmentShader shader;
  final ui.Image image;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: CustomPaint(
            size: const Size(200, 200),
            painter: SamplerPainter(shader, image),
          ),
        ),
      ),
    );
  }
}

class SamplerPainter extends CustomPainter {
  SamplerPainter(this.shader, this.image);

  final ui.FragmentShader shader;
  final ui.Image image;

  @override
  void paint(Canvas canvas, Size size) {
    // Binding float uniforms by name works on web.
    shader.getUniformVec2('uSize').set(size.width, size.height);

    // Binding the sampler by index works on web:
    //   shader.setImageSampler(0, image);
    // Binding it by name works on mobile/desktop, but throws
    // UnsupportedError on web (both CanvasKit and Skwasm):
    shader.getImageSampler('uTexture').set(image);

    canvas.drawRect(Offset.zero & size, Paint()..shader = shader);
  }

  @override
  bool shouldRepaint(SamplerPainter oldDelegate) =>
      oldDelegate.shader != shader || oldDelegate.image != image;
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
Unsupported operation: getImageSampler is not supported on the web.

Flutter Doctor output

[✓] Flutter (Channel stable, 3.44.5, on Debian GNU/Linux rodete 7.1.6-1rodete1-amd64, locale en_US.UTF-8) [116ms]
    • Flutter version 3.44.5 on channel stable at /usr/local/google/home/dipakkumarj/develop/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f94f4fc76b (3 months ago), 2026-07-06 11:19:24 -0700
    • Engine revision 83675ed276
    • Dart version 3.12.2
    • DevTools version 2.57.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations,
      enable-native-assets, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration

[!] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [1,553ms]
    • Android SDK at /usr/local/google/home/dipakkumarj/Android/Sdk
    • Emulator version 36.6.11.0 (build_id 15507667) (CL:N/A)
    ✗ cmdline-tools component is missing.
      Try installing or updating Android Studio.
      Alternatively, download the tools from https://developer.android.com/studio#command-line-tools-only and make sure to set the ANDROID_HOME environment
      variable.
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/to/linux-android-setup for more details.

[✓] Chrome - develop for the web [27ms]
    • Chrome at google-chrome

[✗] Linux toolchain - develop for Linux desktop [1,331ms]
    ✗ clang++ is required for Linux development.
      It is likely available from your distribution (e.g.: apt install clang), or can be downloaded from https://releases.llvm.org/
    ✗ CMake is required for Linux development.
      It is likely available from your distribution (e.g.: apt install cmake), or can be downloaded from https://cmake.org/download/
    ✗ ninja is required for Linux development.
      It is likely available from your distribution (e.g.: apt install ninja-build), or can be downloaded from https://github.com/ninja-build/ninja/releases
    • pkg-config version 2.5.1
    ✗ GTK 3.0 development libraries are required for Linux development.
      They are likely available from your distribution (e.g.: apt install libgtk-3-dev)
    • OpenGL core renderer: llvmpipe (LLVM 21.1.8, 256 bits)
    • OpenGL core version: 4.6 (Core Profile) Mesa 26.1.6-1
    • OpenGL core shading language version: 4.60
    • OpenGL ES renderer: llvmpipe (LLVM 21.1.8, 256 bits)
    • OpenGL ES version: OpenGL ES 3.2 Mesa 26.1.6-1
    • OpenGL ES shading language version: OpenGL ES GLSL ES 3.20
    • GL_EXT_framebuffer_blit: yes
    • GL_EXT_texture_format_BGRA8888: yes

[✓] Connected device (2 available) [2.5s]
    • Linux (desktop) • linux  • linux-x64      • Debian GNU/Linux rodete 7.1.6-1rodete1-amd64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 155.0.8059.39

[✓] Network resources [446ms]
    • All expected network resources are available.

! Doctor found issues in 2 categories.

Activity

  1. saurabh-mirajkar commented on Oct 13, 2026

    @saurabh-mirajkar
    Contributor

    @Dipakkumarjena Thanks for the report. I was able to reproduce this on CanvasKit using Flutter 3.47.7 stable. However, I could not reproduce the same failure on Skwasm in my testing. Keeping this open for the team to evaluate further

  2. added
    engineflutter/engine related. See also e: labels.
    platform-webWeb applications specifically
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    team-webOwned by Web platform team
    and removed
    in triagePresently being triaged by the triage team
    on Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

assigned for triageissue is assigned to a domain expert for further triageengineflutter/engine related. See also e: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-webWeb applications specificallyteam-webOwned by Web platform team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions