Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
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 lib/web_ui/lib/src/engine/compositor/initialization.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ part of engine;
const bool experimentalUseSkia =
bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false);

// If set to true, forces CPU-only rendering (i.e. no WebGL).
const bool canvasKitForceCpuOnly =
bool.fromEnvironment('FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', defaultValue: false);

/// The URL to use when downloading the CanvasKit script and associated wasm.
///
/// When CanvasKit pushes a new release to NPM, update this URL to reflect the
Expand Down
112 changes: 77 additions & 35 deletions lib/web_ui/lib/src/engine/compositor/surface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ class Surface {
SurfaceFrame acquireFrame(ui.Size size) {
final CkSurface surface = acquireRenderSurface(size);

canvasKit.callMethod('setCurrentContext', <int?>[surface.context]);
if (surface.context != null) {
canvasKit.callMethod('setCurrentContext', <int?>[surface.context]);
}
SubmitCallback submitCallback =
(SurfaceFrame surfaceFrame, CkCanvas canvas) {
return _presentSurface();
Expand Down Expand Up @@ -119,51 +121,80 @@ class Surface {
..position = 'absolute'
..width = '${logicalSize.width.ceil()}px'
..height = '${logicalSize.height.ceil()}px';
final int glContext = canvasKit.callMethod('GetWebGLContext', <dynamic>[
htmlCanvas,
// Default to no anti-aliasing. Paint commands can be explicitly
// anti-aliased by setting their `Paint` object's `antialias` property.
js.JsObject.jsify({'antialias': 0}),
]);
_grContext =
canvasKit.callMethod('MakeGrContext', <dynamic>[glContext]);

if (_grContext == null) {
throw CanvasKitError('Could not create a graphics context.');
}

// Set the cache byte limit for this grContext, if not specified it will use
// CanvasKit's default.
_syncCacheBytes();
htmlElement = htmlCanvas;
if (canvasKitForceCpuOnly) {
return _makeSoftwareCanvasSurface(htmlCanvas);
} else {
// Try WebGL first.
final int glContext = canvasKit.callMethod('GetWebGLContext', <dynamic>[
htmlCanvas,
// Default to no anti-aliasing. Paint commands can be explicitly
// anti-aliased by setting their `Paint` object's `antialias` property.
js.JsObject.jsify({'antialias': 0}),
]);

if (glContext == 0) {
return _makeSoftwareCanvasSurface(htmlCanvas);
}

_grContext =
canvasKit.callMethod('MakeGrContext', <dynamic>[glContext]);

final js.JsObject? skSurface =
canvasKit.callMethod('MakeOnScreenGLSurface', <dynamic>[
_grContext,
size.width,
size.height,
canvasKit['SkColorSpace']['SRGB'],
]);
if (_grContext == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we were able to create a GL context but failed to create a graphics context then something is seriously wrong. I think we should throw here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

throw CanvasKitError('Failed to initialize CanvasKit. CanvasKit.MakeGrContext returned null.');
}

// Set the cache byte limit for this grContext, if not specified it will use
// CanvasKit's default.
_syncCacheBytes();

js.JsObject? skSurface =
canvasKit.callMethod('MakeOnScreenGLSurface', <dynamic>[
_grContext,
size.width,
size.height,
canvasKit['SkColorSpace']['SRGB'],
]);

if (skSurface == null) {
return _makeSoftwareCanvasSurface(htmlCanvas);
}

if (skSurface == null) {
throw CanvasKitError('Could not create a surface.');
return CkSurface(skSurface!, _grContext, glContext);
}
}

htmlElement = htmlCanvas;
return CkSurface(skSurface, _grContext!, glContext);
static bool _didWarnAboutWebGlInitializationFailure = false;

CkSurface _makeSoftwareCanvasSurface(html.CanvasElement htmlCanvas) {
if (!_didWarnAboutWebGlInitializationFailure) {
html.window.console.warn('WARNING: failed to initialize WebGL. Falling back to CPU-only rendering.');
_didWarnAboutWebGlInitializationFailure = true;
}
return CkSurface(
canvasKit.callMethod('MakeSWCanvasSurface', <dynamic>[
htmlCanvas,
]),
null,
null,
);
}

bool _presentSurface() {
canvasKit.callMethod('setCurrentContext', <int>[_surface!.context]);
_surface!.getCanvas().flush();
if (_surface!.context != null) {
canvasKit.callMethod('setCurrentContext', <int?>[_surface!.context]);
}
_surface!.flush();
return true;
}
}

/// A Dart wrapper around Skia's CkSurface.
class CkSurface {
final js.JsObject _surface;
final js.JsObject _grContext;
final int _glContext;
final js.JsObject? _grContext;
final int? _glContext;

CkSurface(this._surface, this._grContext, this._glContext);

Expand All @@ -174,7 +205,12 @@ class CkSurface {
);
}

int get context => _glContext;
/// Flushes the graphics to be rendered on screen.
void flush() {
_surface.callMethod('flush');
}

int? get context => _glContext;

int width() => _surface.callMethod('width');
int height() => _surface.callMethod('height');
Expand All @@ -184,10 +220,16 @@ class CkSurface {
return;
}
// Only resources from the current context can be disposed.
canvasKit.callMethod('setCurrentContext', <int>[_glContext]);
if (_glContext != null) {
canvasKit.callMethod('setCurrentContext', <int?>[_glContext]);
}
_surface.callMethod('dispose');
_grContext.callMethod('releaseResourcesAndAbandonContext');
_grContext.callMethod('delete');

// In CPU-only mode there's no graphics context.
if (_grContext != null) {
_grContext!.callMethod('releaseResourcesAndAbandonContext');
_grContext!.callMethod('delete');
}
_isDisposed = true;
}

Expand Down