mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Reuse the same temporary canvas for creating shader images (flutter/engine#32864)
This commit is contained in:
parent
264868b89b
commit
af7fb1d68b
@ -22,6 +22,27 @@ import 'vertex_shaders.dart';
|
||||
const double kFltEpsilon = 1.19209290E-07; // == 1 / (2 ^ 23)
|
||||
const double kFltEpsilonSquared = 1.19209290E-07 * 1.19209290E-07;
|
||||
|
||||
class SharedCanvas {
|
||||
OffScreenCanvas? _canvas;
|
||||
bool _checkedOut = false;
|
||||
GlContext checkOutContext(int width, int height) {
|
||||
assert(!_checkedOut);
|
||||
_checkedOut = true;
|
||||
if(_canvas == null) {
|
||||
_canvas = OffScreenCanvas(width, height);
|
||||
} else {
|
||||
_canvas!.resize(width, height);
|
||||
}
|
||||
return GlContext(_canvas!);
|
||||
}
|
||||
|
||||
void checkInContext() {
|
||||
assert(_checkedOut);
|
||||
_checkedOut = false;
|
||||
}
|
||||
}
|
||||
SharedCanvas _sharedCanvas = SharedCanvas();
|
||||
|
||||
abstract class EngineGradient implements ui.Gradient {
|
||||
/// Hidden constructor to prevent subclassing.
|
||||
EngineGradient._();
|
||||
@ -58,9 +79,7 @@ class GradientSweep extends EngineGradient {
|
||||
|
||||
initWebGl();
|
||||
// Render gradient into a bitmap and create a canvas pattern.
|
||||
final OffScreenCanvas offScreenCanvas =
|
||||
OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
final GlContext gl = GlContext(offScreenCanvas);
|
||||
final GlContext gl = _sharedCanvas.checkOutContext(widthInPixels, heightInPixels);
|
||||
gl.setViewportSize(widthInPixels, heightInPixels);
|
||||
|
||||
final NormalizedGradient normalizedGradient =
|
||||
@ -82,23 +101,27 @@ class GradientSweep extends EngineGradient {
|
||||
final Object gradientMatrix =
|
||||
gl.getUniformLocation(glProgram.program, 'm_gradient');
|
||||
gl.setUniformMatrix4fv(gradientMatrix, false, matrix4 ?? Matrix4.identity().storage);
|
||||
if (createDataUrl) {
|
||||
return glRenderer!.drawRectToImageUrl(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height),
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels);
|
||||
} else {
|
||||
return glRenderer!.drawRect(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height),
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels)!;
|
||||
}
|
||||
final Object result = () {
|
||||
if (createDataUrl) {
|
||||
return glRenderer!.drawRectToImageUrl(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height),
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels);
|
||||
} else {
|
||||
return glRenderer!.drawRect(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height),
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels)!;
|
||||
}
|
||||
}();
|
||||
_sharedCanvas.checkInContext();
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -222,9 +245,7 @@ class GradientLinear extends EngineGradient {
|
||||
assert(widthInPixels > 0 && heightInPixels > 0);
|
||||
initWebGl();
|
||||
// Render gradient into a bitmap and create a canvas pattern.
|
||||
final OffScreenCanvas offScreenCanvas =
|
||||
OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
final GlContext gl = GlContext(offScreenCanvas);
|
||||
final GlContext gl = _sharedCanvas.checkOutContext(widthInPixels, heightInPixels);
|
||||
gl.setViewportSize(widthInPixels, heightInPixels);
|
||||
|
||||
final NormalizedGradient normalizedGradient =
|
||||
@ -314,27 +335,31 @@ class GradientLinear extends EngineGradient {
|
||||
final Object uRes = gl.getUniformLocation(glProgram.program, 'u_resolution');
|
||||
gl.setUniform2f(uRes, widthInPixels.toDouble(), heightInPixels.toDouble());
|
||||
|
||||
if (createDataUrl) {
|
||||
return glRenderer!.drawRectToImageUrl(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width,
|
||||
shaderBounds.height) /* !! shaderBounds */,
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels,
|
||||
);
|
||||
} else {
|
||||
return glRenderer!.drawRect(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width,
|
||||
shaderBounds.height) /* !! shaderBounds */,
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels,
|
||||
)!;
|
||||
}
|
||||
final Object result = () {
|
||||
if (createDataUrl) {
|
||||
return glRenderer!.drawRectToImageUrl(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width,
|
||||
shaderBounds.height) /* !! shaderBounds */,
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels,
|
||||
);
|
||||
} else {
|
||||
return glRenderer!.drawRect(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width,
|
||||
shaderBounds.height) /* !! shaderBounds */,
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels,
|
||||
)!;
|
||||
}
|
||||
}();
|
||||
_sharedCanvas.checkInContext();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Creates a linear gradient with tiling repeat or mirror.
|
||||
@ -490,9 +515,7 @@ class GradientRadial extends EngineGradient {
|
||||
|
||||
initWebGl();
|
||||
// Render gradient into a bitmap and create a canvas pattern.
|
||||
final OffScreenCanvas offScreenCanvas =
|
||||
OffScreenCanvas(widthInPixels, heightInPixels);
|
||||
final GlContext gl = GlContext(offScreenCanvas);
|
||||
final GlContext gl = _sharedCanvas.checkOutContext(widthInPixels, heightInPixels);
|
||||
gl.setViewportSize(widthInPixels, heightInPixels);
|
||||
|
||||
final NormalizedGradient normalizedGradient =
|
||||
@ -519,23 +542,27 @@ class GradientRadial extends EngineGradient {
|
||||
gl.setUniformMatrix4fv(gradientMatrix, false,
|
||||
matrix4 == null ? Matrix4.identity().storage : matrix4!);
|
||||
|
||||
if (createDataUrl) {
|
||||
return glRenderer!.drawRectToImageUrl(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height),
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels);
|
||||
} else {
|
||||
return glRenderer!.drawRect(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height),
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels)!;
|
||||
}
|
||||
final Object result = () {
|
||||
if (createDataUrl) {
|
||||
return glRenderer!.drawRectToImageUrl(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height),
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels);
|
||||
} else {
|
||||
return glRenderer!.drawRect(
|
||||
ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height),
|
||||
gl,
|
||||
glProgram,
|
||||
normalizedGradient,
|
||||
widthInPixels,
|
||||
heightInPixels)!;
|
||||
}
|
||||
}();
|
||||
_sharedCanvas.checkInContext();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Creates a radial gradient with tiling repeat or mirror.
|
||||
|
||||
@ -966,12 +966,31 @@ class OffScreenCanvas {
|
||||
height: height,
|
||||
);
|
||||
canvasElement!.className = 'gl-canvas';
|
||||
final double cssWidth = width / EnginePlatformDispatcher.browserDevicePixelRatio;
|
||||
final double cssHeight = height / EnginePlatformDispatcher.browserDevicePixelRatio;
|
||||
canvasElement!.style
|
||||
..position = 'absolute'
|
||||
..width = '${cssWidth}px'
|
||||
..height = '${cssHeight}px';
|
||||
_updateCanvasCssSize(canvasElement!);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateCanvasCssSize(html.CanvasElement element) {
|
||||
final double cssWidth = width / EnginePlatformDispatcher.browserDevicePixelRatio;
|
||||
final double cssHeight = height / EnginePlatformDispatcher.browserDevicePixelRatio;
|
||||
element.style
|
||||
..position = 'absolute'
|
||||
..width = '${cssWidth}px'
|
||||
..height = '${cssHeight}px';
|
||||
}
|
||||
|
||||
void resize(int requestedWidth, int requestedHeight) {
|
||||
if(requestedWidth != width && requestedHeight != height) {
|
||||
width = requestedWidth;
|
||||
height = requestedHeight;
|
||||
if(offScreenCanvas != null) {
|
||||
offScreenCanvas!.width = requestedWidth;
|
||||
offScreenCanvas!.height = requestedHeight;
|
||||
} else if (canvasElement != null) {
|
||||
canvasElement!.width = requestedWidth;
|
||||
canvasElement!.height = requestedHeight;
|
||||
_updateCanvasCssSize(canvasElement!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import 'dart:html' as html;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:web_gl';
|
||||
|
||||
import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:test/test.dart';
|
||||
@ -390,6 +391,38 @@ Future<void> testMain() async {
|
||||
canvas.apply(engineCanvas, screenRect);
|
||||
});
|
||||
|
||||
test('Creating lots of gradients doesn\'t create too many webgl contexts',
|
||||
() async {
|
||||
final html.CanvasElement sideCanvas =
|
||||
html.CanvasElement(width: 5, height: 5);
|
||||
final RenderingContext? context =
|
||||
sideCanvas.getContext('webgl') as RenderingContext?;
|
||||
expect(context, isNotNull);
|
||||
|
||||
final EngineCanvas engineCanvas =
|
||||
BitmapCanvas(const Rect.fromLTRB(0, 0, 100, 100), RenderStrategy());
|
||||
for (double x = 0; x < 100; x += 10) {
|
||||
for (double y = 0; y < 100; y += 10) {
|
||||
const List<Color> colors = <Color>[
|
||||
Color(0xFFFF0000),
|
||||
Color(0xFF0000FF),
|
||||
];
|
||||
|
||||
final GradientLinear linearGradient = GradientLinear(
|
||||
const Offset(0, 0),
|
||||
const Offset(10, 10),
|
||||
colors,
|
||||
null,
|
||||
TileMode.clamp,
|
||||
Matrix4.identity().storage);
|
||||
engineCanvas.drawRect(Rect.fromLTWH(x, y, 10, 10),
|
||||
SurfacePaintData()..shader = linearGradient);
|
||||
}
|
||||
}
|
||||
|
||||
expect(context!.isContextLost(), isFalse);
|
||||
}, skip: isFirefox);
|
||||
|
||||
test('Paints clamped, rotated and shifted linear gradient', () async {
|
||||
final RecordingCanvas canvas =
|
||||
RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user