Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1944,6 +1944,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.d
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/texture.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/util.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/vertices.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/clipboard.dart + ../../../flutter/LICENSE
Expand Down Expand Up @@ -2066,6 +2067,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/input_action.dar
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/input_type.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/texture.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/util.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/validators.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/vector_math.dart + ../../../flutter/LICENSE
Expand Down Expand Up @@ -4426,6 +4428,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dar
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/texture.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/util.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/vertices.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/clipboard.dart
Expand Down Expand Up @@ -4548,6 +4551,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/input_action.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/input_type.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/texture.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/util.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/validators.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/vector_math.dart
Expand Down
29 changes: 29 additions & 0 deletions lib/web_ui/lib/initialization.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,32 @@ class PlatformViewRegistry {

/// The platform view registry for this app.
final PlatformViewRegistry platformViewRegistry = PlatformViewRegistry();

/// A registry for textures.
class TextureRegistry {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are working on removing web-specific API from dart:ui because it confuses compilers and code analyzers. Instead, we expose API through JS-interop. Let's consult with @ditman on how best to do this for textures. We might want to do it in a way that's easy to expose through flutter.js API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Realistically we don't have a good place to put this in a way that is ergonomic for users to use (and a workaround for the analyzer to freak out less when using these web-only APIs).

In practice, the API surface of dart:ui has barely changed in the last couple of years, and adding one more object to the mix is not too bad.

@yjbanov I think this can land in dart:ui, and whenever we come up with a better location for all these extra methods (dart:web_ui?), move them there. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ditman Is there any way we could have two separate, clearly named libraries in the future, something like dart:native_ui and dart:web_ui? I feel like dart:ui in and of itself has been a point of confusion for too long for those working with the web platform as a target. I would assume that for backwards compatibility (if necessary) dart:ui could just re-export (certain members of) one or both - much like Rust's std re-exports (almost?) all of the core package members.

Personally, I would love for these to be named in a way that clearly shows they're building blocks for Flutter, and not really useful on their own when using pure Dart, but that might be too big a deviation from the norm at this point. I don't know, for example, if it would be possible technically to implement packages prefixed with flutter, like flutter:native and flutter:web.

Long story short, I would appreciate a clear separation between Dart and Flutter, where one ends and the other begins, and the same for native and web targets. But I'm sure this isn't the place to discuss this in detail, so I'll be looking out for issues related to this in the future, if any.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@skreborn you're not alone, and this is such a big change that you'll know when it's coming for sure! The decision of making the flutter engine (dart:ui) a dart library predates me, by years! I guess it's because the engine needs some extra help from the Dart compilers, but I haven't dug too deep.

(I'd be happy if we could get to a point where dart:ui does not have anything web-specific, and dart:web_ui (or whatever its final name is) has only web-specific things, so it's clear from your imports if you're calling web-only APIs. Not sure what needs to be done for that to happen, haven't looked into the structural bits of the code yet!)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS: We now have a dart:ui_web package for things like this 😅

Thank @eyebrowsoffire and @mdebbar!

/// Registers a texture.
///
/// Valid [texture] can be any object that that webGL's texImage2D supports.
/// For example, HTMLImageElement, HTMLCanvasElement, HTMLVideoElement,
/// ImageData, ImageBitmap and VideoFrame.
///
/// The returned id can be used to refer to the texture in Texture widgets.
int registerTexture(Object texture) {
return engine.renderer.textureRegistry?.registerTexture(texture) ?? -1;
}

/// Unregisters the texture with the given id.
void unregisterTexture(int id) {
engine.renderer.textureRegistry?.unregisterTexture(id);
}

/// Notifies Flutter that the content of the previously registered texture
/// has been updated.
void textureFrameAvailable(int id) {
engine.renderer.textureRegistry?.textureFrameAvailable(id);
engine.EnginePlatformDispatcher.instance.scheduleFrame();
}
}

/// The texture registry for this app.
final TextureRegistry textureRegistry = TextureRegistry();
2 changes: 2 additions & 0 deletions lib/web_ui/lib/src/engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export 'engine/canvaskit/skia_object_cache.dart';
export 'engine/canvaskit/surface.dart';
export 'engine/canvaskit/surface_factory.dart';
export 'engine/canvaskit/text.dart';
export 'engine/canvaskit/texture.dart';
export 'engine/canvaskit/util.dart';
export 'engine/canvaskit/vertices.dart';
export 'engine/clipboard.dart';
Expand Down Expand Up @@ -168,6 +169,7 @@ export 'engine/text_editing/input_action.dart';
export 'engine/text_editing/input_type.dart';
export 'engine/text_editing/text_capitalization.dart';
export 'engine/text_editing/text_editing.dart';
export 'engine/texture.dart';
export 'engine/util.dart';
export 'engine/validators.dart';
export 'engine/vector_math.dart';
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ extension CanvasKitExtension on CanvasKit {
);
external SkImage? MakeLazyImageFromTextureSource(
Object src,
SkPartialImageInfo info,
SkPartialImageInfo? info,
);
}

Expand Down
30 changes: 30 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'painting.dart';
import 'path.dart';
import 'picture.dart';
import 'raster_cache.dart';
import 'texture.dart';
import 'util.dart';

/// A layer to be composed into a scene.
Expand Down Expand Up @@ -87,6 +88,7 @@ class PaintContext {
this.leafNodesCanvas,
this.rasterCache,
this.viewEmbedder,
this.textureRegistry,
);

/// A multi-canvas that applies clips, transforms, and opacity
Expand All @@ -102,6 +104,8 @@ class PaintContext {

/// A compositor for embedded HTML views.
final HtmlViewEmbedder? viewEmbedder;

final CkTextureRegistry? textureRegistry;
}

/// A layer that contains child layers.
Expand Down Expand Up @@ -616,3 +620,29 @@ class PlatformViewLayer extends Layer {
}
}
}

/// A layer which renders a texture backed by [textureId].
class TextureLayer extends Layer {
TextureLayer(this.textureId, this.offset, this.width, this.height,
this.freeze, this.filterQuality);

final int textureId;
final ui.Offset offset;
final double width;
final double height;
final bool freeze;
final ui.FilterQuality filterQuality;

@override
void preroll(PrerollContext prerollContext, Matrix4 matrix) {
paintBounds = ui.Rect.fromLTWH(offset.dx, offset.dy, width, height);
}

@override
void paint(PaintContext paintContext) {
final CkTexture? texture =
paintContext.textureRegistry?.getTexture(textureId);

texture?.paint(paintContext, offset, width, height, freeze, filterQuality);
}
}
4 changes: 3 additions & 1 deletion lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ class LayerSceneBuilder implements ui.SceneBuilder {
bool freeze = false,
ui.FilterQuality filterQuality = ui.FilterQuality.low,
}) {
// TODO(hterkelsen): implement addTexture, b/128315641
currentLayer.add(
TextureLayer(textureId, offset, width, height, freeze, filterQuality),
);
}

@override
Expand Down
23 changes: 18 additions & 5 deletions lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'layer.dart';
import 'n_way_canvas.dart';
import 'picture_recorder.dart';
import 'raster_cache.dart';
import 'texture.dart';

/// A tree of [Layer]s that, together with a [Size] compose a frame.
class LayerTree {
Expand Down Expand Up @@ -58,6 +59,7 @@ class LayerTree {
frame.canvas,
ignoreRasterCache ? null : frame.rasterCache,
frame.viewEmbedder,
frame.textureRegistry,
);
if (rootLayer.needsPainting) {
rootLayer.paint(context);
Expand All @@ -75,8 +77,13 @@ class LayerTree {

final CkNWayCanvas internalNodesCanvas = CkNWayCanvas();
internalNodesCanvas.addCanvas(canvas);
final PaintContext paintContext =
PaintContext(internalNodesCanvas, canvas, null, null);
final PaintContext paintContext = PaintContext(
internalNodesCanvas,
canvas,
null,
null,
CkTextureRegistry.instance,
);
if (rootLayer.needsPainting) {
rootLayer.paint(paintContext);
}
Expand All @@ -86,7 +93,7 @@ class LayerTree {

/// A single frame to be rendered.
class Frame {
Frame(this.canvas, this.rasterCache, this.viewEmbedder);
Frame(this.canvas, this.rasterCache, this.viewEmbedder, this.textureRegistry);

/// The canvas to render this frame to.
final CkCanvas canvas;
Expand All @@ -97,6 +104,8 @@ class Frame {
/// The platform view embedder.
final HtmlViewEmbedder? viewEmbedder;

final CkTextureRegistry? textureRegistry;

/// Rasterize the given layer tree into this frame.
bool raster(LayerTree layerTree, {bool ignoreRasterCache = false}) {
timeAction<void>(kProfilePrerollFrame, () {
Expand All @@ -115,7 +124,11 @@ class CompositorContext {
RasterCache? rasterCache;

/// Acquire a frame using this compositor's settings.
Frame acquireFrame(CkCanvas canvas, HtmlViewEmbedder? viewEmbedder) {
return Frame(canvas, rasterCache, viewEmbedder);
Frame acquireFrame(
CkCanvas canvas,
HtmlViewEmbedder? viewEmbedder,
CkTextureRegistry? textureRegistry,
) {
return Frame(canvas, rasterCache, viewEmbedder, textureRegistry);
}
}
8 changes: 6 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'embedded_views.dart';
import 'layer_tree.dart';
import 'surface.dart';
import 'surface_factory.dart';
import 'texture.dart';

/// A class that can rasterize [LayerTree]s into a given [Surface].
class Rasterizer {
Expand All @@ -33,8 +34,11 @@ class Rasterizer {
SurfaceFactory.instance.baseSurface.acquireFrame(layerTree.frameSize);
HtmlViewEmbedder.instance.frameSize = layerTree.frameSize;
final CkCanvas canvas = frame.skiaCanvas;
final Frame compositorFrame =
context.acquireFrame(canvas, HtmlViewEmbedder.instance);
final Frame compositorFrame = context.acquireFrame(
canvas,
HtmlViewEmbedder.instance,
CkTextureRegistry.instance,
);

compositorFrame.raster(layerTree, ignoreRasterCache: true);
SurfaceFactory.instance.baseSurface.addToScene();
Expand Down
4 changes: 4 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'picture_recorder.dart';
import 'rasterizer.dart';
import 'shader.dart';
import 'text.dart';
import 'texture.dart';
import 'vertices.dart';

class CanvasKitRenderer implements Renderer {
Expand All @@ -40,6 +41,9 @@ class CanvasKitRenderer implements Renderer {
@override
SkiaFontCollection get fontCollection => _fontCollection;

@override
final CkTextureRegistry textureRegistry = CkTextureRegistry.instance;

/// The scene host, where the root canvas and overlay canvases are added to.
DomElement? _sceneHost;
DomElement? get sceneHost => _sceneHost;
Expand Down
99 changes: 99 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/texture.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:ui/ui.dart' as ui;

import '../texture.dart';
import 'canvaskit_api.dart';
import 'image.dart';
import 'layer.dart';
import 'painting.dart';

class CkTexture {
CkTexture(this.source);

final Object source;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if accepting all possible texture sources is future-proof enough. In particular, Skwasm will move to a multi-threaded rendering model where GPU work will be done through an OffscreenCanvas in a web worker. I'm not sure if in that model we can use <img> elements as texture sources since there's no DOM in web workers. Maybe there's a way to first convert <img> to something that's transferable, such as VideoFrame, and then send it to the worker? cc @eyebrowsoffire for guidance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue you bring up is something that I'm going to have to work out regardless of this change, and I haven't gotten to the point with the multi-threaded rendering where I have to decide how I'm going to solve it, but I think this should be fine. A VideoFrame can be constructed from an HTMLImageElement if we end up going that route: https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame/VideoFrame


CkImage? _ckImage;

bool _newFrameReady = false;

final CkPaint _paint = CkPaint();

void markNewFrameAvailable() {
_newFrameReady = true;
}

void paint(
PaintContext context,
ui.Offset offset,
double width,
double height,
bool freeze,
ui.FilterQuality filterQuality,
) {
if (_ckImage == null || _newFrameReady && !freeze) {
final SkImage? skImage =
canvasKit.MakeLazyImageFromTextureSource(source, null);

if (skImage == null) {
return;
}

_ckImage?.dispose();
_ckImage = CkImage(skImage);
}

context.leafNodesCanvas?.drawImageRect(
_ckImage!,
ui.Rect.fromLTWH(
0,
0,
_ckImage!.width.toDouble(),
_ckImage!.height.toDouble(),
),
ui.Rect.fromLTWH(offset.dx, offset.dy, width, height),
_paint..filterQuality = filterQuality,
);
}

void dispose() {
_ckImage?.dispose();
}
}

class CkTextureRegistry implements TextureRegistry {
CkTextureRegistry._();

static final CkTextureRegistry instance = CkTextureRegistry._();

final Map<int, CkTexture> _textures = <int, CkTexture>{};

int _nextTextureId = 0;

@override
int registerTexture(Object source) {
final int id = _nextTextureId++;
_textures[id] = CkTexture(source);
return id;
}

@override
void unregisterTexture(int id) {
final CkTexture? texture = _textures.remove(id);
texture?.dispose();
}

CkTexture? getTexture(int id) {
return _textures[id];
}

@override
void textureFrameAvailable(int id) {
final CkTexture? texture = _textures[id];
if (texture != null) {
texture.markNewFrameAvailable();
}
}
}
3 changes: 3 additions & 0 deletions lib/web_ui/lib/src/engine/html/renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class HtmlRenderer implements Renderer {
@override
HtmlFontCollection get fontCollection => _fontCollection;

@override
TextureRegistry? get textureRegistry => null;

@override
void initialize() {
scheduleMicrotask(() {
Expand Down
3 changes: 3 additions & 0 deletions lib/web_ui/lib/src/engine/renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'fonts.dart';
import 'html/renderer.dart';
import 'html_image_codec.dart';
import 'skwasm/skwasm_stub/renderer.dart' if (dart.library.ffi) 'skwasm/skwasm_impl/renderer.dart';
import 'texture.dart';

final Renderer _renderer = Renderer._internal();
Renderer get renderer => _renderer;
Expand Down Expand Up @@ -49,6 +50,8 @@ abstract class Renderer {
String get rendererTag;
FontCollection get fontCollection;

TextureRegistry? get textureRegistry;

FutureOr<void> initialize();
void reset(FlutterViewEmbedder embedder);

Expand Down
Loading