Skip to content

[web:canvaskit] Image.toByteData loses track of WebGL contexts corrupting rendering pipeline state #121758

@yjbanov

Description

@yjbanov

The following web engine test reproduces the issue:

Key parts of the test:

  • Record a Picture (the test renders string "Hello" with a green background).
  • Call Picture.toImageSync to generate an Image from the picture.
  • Call Image.toByteData to encode the image as PNG. The byte data can be discarded. This step is needed to cause Skia to use resources in a temporary GL context created to save the image data.
  • Render a scene that shows the picture, followed by a platform view, followed by the image. The contents of the platform view are not important. We just need to spread resources across multiple GL contexts.

Expected

See two copies of the picture, one rendered from the picture, and a second one rendered from the image.

Actual

The image copy is missing. You may also see the following message printed in the browser console:

WebGL: INVALID_OPERATION: bindTexture: object does not belong to this context
WebGL: INVALID_OPERATION: bindFramebuffer: object does not belong to this context

Other manifestations are possible depending on what the app does. It seems in general this causes a corruption in the rendering pipeline, so all bets are off. For example, in another context the behavior was broken text rendering that looks like this:

location-actual

(this should render the word "location")

Test code

    test('resources used in temporary surfaces for Image.toByteData can cross to rendering overlays', () async {
      final Rasterizer rasterizer = CanvasKitRenderer.instance.rasterizer;
      SurfaceFactory.instance.debugClear();

      ui.platformViewRegistry.registerViewFactory(
        'test-platform-view',
        (int viewId) => createDomHTMLDivElement()..id = 'view-0',
      );
      await createPlatformView(0, 'test-platform-view');

      CkPicture makeTextPicture(String text, ui.Offset offset) {
        final CkPictureRecorder recorder = CkPictureRecorder();
        final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest);
        final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle());
        builder.addText(text);
        final CkParagraph paragraph = builder.build();
        paragraph.layout(const ui.ParagraphConstraints(width: 1000));
        canvas.drawRect(
          ui.Rect.fromLTWH(offset.dx, offset.dy, paragraph.width, paragraph.height).inflate(10),
          CkPaint()..color = const ui.Color(0xFF00FF00)
        );
        canvas.drawParagraph(paragraph, offset);
        return recorder.endRecording();
      }

      CkPicture imageToPicture(CkImage image, ui.Offset offset) {
        final CkPictureRecorder recorder = CkPictureRecorder();
        final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest);
        canvas.drawImage(image, offset, CkPaint());
        return recorder.endRecording();
      }

      final CkPicture helloPicture = makeTextPicture('Hello', ui.Offset.zero);

      final CkImage helloImage = helloPicture.toImageSync(500, 500);

      // Calling toByteData on an image created from a picture used to hit the
      // bug.
      await helloImage.toByteData(format: ui.ImageByteFormat.png);

      final LayerSceneBuilder sb = LayerSceneBuilder();
      sb.pushOffset(0, 0);
      sb.addPicture(ui.Offset.zero, helloPicture);
      sb.addPlatformView(0, width: 10, height: 10);
      sb.addPicture(const ui.Offset(0, 50), imageToPicture(helloImage, ui.Offset.zero));
      sb.pop();

      // The below line should not throw an error.
      rasterizer.draw(sb.build().layerTree);

      await matchGoldenFile('cross_overlay_resources.png');
      await Future<void>.delayed(const Duration(seconds: 20));
    });

Internal issue: b/269725835

Metadata

Metadata

Assignees

Labels

P0Critical issues such as a build break or regressioncustomer: huggsy (g3)e: web_canvaskitCanvasKit (a.k.a. Skia-on-WebGL) rendering backend for Webengineflutter/engine related. See also e: labels.platform-webWeb applications specifically

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions