Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
sxychenjing
engine
提交
3d4c021f
E
engine
项目概览
sxychenjing
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
engine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
3d4c021f
编写于
12月 15, 2020
作者:
Y
Yegor
提交者:
GitHub
12月 15, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[canvaskit] cache and reuse platform view overlays (#23061)
上级
3d3e16e3
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
305 addition
and
56 deletion
+305
-56
lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
+98
-29
lib/web_ui/lib/src/engine/canvaskit/surface.dart
lib/web_ui/lib/src/engine/canvaskit/surface.dart
+63
-21
lib/web_ui/test/canvaskit/common.dart
lib/web_ui/test/canvaskit/common.dart
+1
-0
lib/web_ui/test/canvaskit/embedded_views_test.dart
lib/web_ui/test/canvaskit/embedded_views_test.dart
+104
-0
lib/web_ui/test/canvaskit/surface_test.dart
lib/web_ui/test/canvaskit/surface_test.dart
+39
-6
未找到文件。
lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
浏览文件 @
3d4c021f
...
...
@@ -30,14 +30,16 @@ class HtmlViewEmbedder {
/// The root view in the stack of mutator elements for the view id.
final
Map
<
int
?,
html
.
Element
?>
_rootViews
=
<
int
?,
html
.
Element
?>{};
/// The overlay for the view id.
final
Map
<
int
,
Overlay
>
_overlays
=
<
int
,
Overlay
>{};
/// Surfaces used to draw on top of platform views, keyed by platform view ID.
///
/// These surfaces are cached in the [OverlayCache] and reused.
final
Map
<
int
,
Surface
>
_overlays
=
<
int
,
Surface
>{};
/// The views that need to be recomposited into the scene on the next frame.
final
Set
<
int
>
_viewsToRecomposite
=
<
int
>{};
/// The views that need to be disposed of on the next frame.
final
Set
<
int
?>
_viewsToDispose
=
<
int
?
>{};
final
Set
<
int
>
_viewsToDispose
=
<
int
>{};
/// The list of view ids that should be composited, in order.
List
<
int
>
_compositionOrder
=
<
int
>[];
...
...
@@ -115,14 +117,15 @@ class HtmlViewEmbedder {
void
_dispose
(
MethodCall
methodCall
,
ui
.
PlatformMessageResponseCallback
callback
)
{
int
?
viewId
=
methodCall
.
arguments
;
final
int
?
viewId
=
methodCall
.
arguments
;
const
MethodCodec
codec
=
StandardMethodCodec
();
if
(!
_views
.
containsKey
(
viewId
))
{
if
(
viewId
==
null
||
!
_views
.
containsKey
(
viewId
))
{
callback
(
codec
.
encodeErrorEnvelope
(
code:
'unknown_view'
,
message:
'trying to dispose an unknown view'
,
details:
'view id:
$viewId
'
,
));
return
;
}
_viewsToDispose
.
add
(
viewId
);
callback
(
codec
.
encodeSuccessEnvelope
(
null
));
...
...
@@ -339,9 +342,9 @@ class HtmlViewEmbedder {
for
(
int
i
=
0
;
i
<
_compositionOrder
.
length
;
i
++)
{
int
viewId
=
_compositionOrder
[
i
];
ensureOverlayInitialized
(
viewId
);
_
ensureOverlayInitialized
(
viewId
);
final
SurfaceFrame
frame
=
_overlays
[
viewId
]!.
surface
.
acquireFrame
(
_frameSize
);
_overlays
[
viewId
]!.
acquireFrame
(
_frameSize
);
final
CkCanvas
canvas
=
frame
.
skiaCanvas
;
canvas
.
drawPicture
(
_pictureRecorders
[
viewId
]!.
endRecording
(),
...
...
@@ -353,12 +356,22 @@ class HtmlViewEmbedder {
_compositionOrder
.
clear
();
return
;
}
final
Set
<
int
>
unusedViews
=
Set
<
int
>.
from
(
_activeCompositionOrder
);
_activeCompositionOrder
.
clear
();
for
(
int
i
=
0
;
i
<
_compositionOrder
.
length
;
i
++)
{
int
viewId
=
_compositionOrder
[
i
];
assert
(
_views
.
containsKey
(
viewId
),
'Cannot render platform view
$viewId
. '
'It has not been created, or it has been deleted.'
,
);
unusedViews
.
remove
(
viewId
);
html
.
Element
platformViewRoot
=
_rootViews
[
viewId
]!;
html
.
Element
overlay
=
_overlays
[
viewId
]!.
surface
.
htmlElement
!
;
html
.
Element
overlay
=
_overlays
[
viewId
]!.
htmlElement
;
platformViewRoot
.
remove
();
skiaSceneHost
!.
append
(
platformViewRoot
);
overlay
.
remove
();
...
...
@@ -366,6 +379,10 @@ class HtmlViewEmbedder {
_activeCompositionOrder
.
add
(
viewId
);
}
_compositionOrder
.
clear
();
for
(
final
int
unusedViewId
in
unusedViews
)
{
_releaseOverlay
(
unusedViewId
);
}
}
void
disposeViews
()
{
...
...
@@ -373,18 +390,12 @@ class HtmlViewEmbedder {
return
;
}
for
(
int
?
viewId
in
_viewsToDispose
)
{
for
(
final
int
viewId
in
_viewsToDispose
)
{
final
html
.
Element
rootView
=
_rootViews
[
viewId
]!;
rootView
.
remove
();
_views
.
remove
(
viewId
);
_rootViews
.
remove
(
viewId
);
if
(
_overlays
[
viewId
]
!=
null
)
{
final
Overlay
overlay
=
_overlays
[
viewId
]!;
overlay
.
surface
.
htmlElement
?.
remove
();
overlay
.
surface
.
htmlElement
=
null
;
overlay
.
skSurface
?.
dispose
();
}
_overlays
.
remove
(
viewId
);
_releaseOverlay
(
viewId
);
_currentCompositionParams
.
remove
(
viewId
);
_clipCount
.
remove
(
viewId
);
_viewsToRecomposite
.
remove
(
viewId
);
...
...
@@ -392,14 +403,80 @@ class HtmlViewEmbedder {
_viewsToDispose
.
clear
();
}
void
ensureOverlayInitialized
(
int
viewId
)
{
Overlay
?
overlay
=
_overlays
[
viewId
];
void
_releaseOverlay
(
int
viewId
)
{
if
(
_overlays
[
viewId
]
!=
null
)
{
OverlayCache
.
instance
.
releaseOverlay
(
_overlays
[
viewId
]!);
_overlays
.
remove
(
viewId
);
}
}
void
_ensureOverlayInitialized
(
int
viewId
)
{
// If there's an active overlay for the view ID, continue using it.
Surface
?
overlay
=
_overlays
[
viewId
];
if
(
overlay
!=
null
)
{
return
;
}
Surface
surface
=
Surface
(
this
);
CkSurface
?
skSurface
=
surface
.
acquireRenderSurface
(
_frameSize
);
_overlays
[
viewId
]
=
Overlay
(
surface
,
skSurface
);
// Try reusing a cached overlay created for another platform view.
overlay
=
OverlayCache
.
instance
.
reserveOverlay
();
// If nothing to reuse, create a new overlay.
if
(
overlay
==
null
)
{
overlay
=
Surface
(
this
);
}
_overlays
[
viewId
]
=
overlay
;
}
}
/// Caches surfaces used to overlay platform views.
class
OverlayCache
{
static
const
int
kDefaultCacheSize
=
5
;
/// The cache singleton.
static
final
OverlayCache
instance
=
OverlayCache
(
kDefaultCacheSize
);
OverlayCache
(
this
.
maximumSize
);
/// The cache will not grow beyond this size.
final
int
maximumSize
;
/// Cached surfaces, available for reuse.
final
List
<
Surface
>
_cache
=
<
Surface
>[];
/// Returns the list of cached surfaces.
///
/// Useful in tests.
List
<
Surface
>
get
debugCachedSurfaces
=>
_cache
;
/// Reserves an overlay from the cache, if available.
///
/// Returns null if the cache is empty.
Surface
?
reserveOverlay
()
{
if
(
_cache
.
isEmpty
)
{
return
null
;
}
return
_cache
.
removeLast
();
}
/// Returns an overlay back to the cache.
///
/// If the cache is full, the overlay is deleted.
void
releaseOverlay
(
Surface
overlay
)
{
overlay
.
htmlElement
.
remove
();
if
(
_cache
.
length
<
maximumSize
)
{
_cache
.
add
(
overlay
);
}
else
{
overlay
.
dispose
();
}
}
int
get
debugLength
=>
_cache
.
length
;
void
debugClear
()
{
for
(
final
Surface
overlay
in
_cache
)
{
overlay
.
dispose
();
}
}
}
...
...
@@ -547,11 +624,3 @@ class MutatorsStack extends Iterable<Mutator> {
@override
Iterator
<
Mutator
>
get
iterator
=>
_mutators
.
reversed
.
iterator
;
}
/// Represents a surface overlaying a platform view.
class
Overlay
{
final
Surface
surface
;
final
CkSurface
?
skSurface
;
Overlay
(
this
.
surface
,
this
.
skSurface
);
}
lib/web_ui/lib/src/engine/canvaskit/surface.dart
浏览文件 @
3d4c021f
...
...
@@ -38,10 +38,27 @@ class Surface {
Surface
(
this
.
viewEmbedder
);
CkSurface
?
_surface
;
html
.
Element
?
htmlElement
;
/// If true, forces a new WebGL context to be created, even if the window
/// size is the same. This is used to restore the UI after the browser tab
/// goes dormant and loses the GL context.
bool
_forceNewContext
=
true
;
bool
get
debugForceNewContext
=>
_forceNewContext
;
SkGrContext
?
_grContext
;
int
?
_skiaCacheBytes
;
/// The root HTML element for this surface.
///
/// This element contains the canvas used to draw the UI. Unlike the canvas,
/// this element is permanent. It is never replaced or deleted, until this
/// surface is disposed of via [dispose].
///
/// Conversely, the canvas that lives inside this element can be swapped, for
/// example, when the screen size changes, or when the WebGL context is lost
/// due to the browser tab becoming dormant.
final
html
.
Element
htmlElement
=
html
.
Element
.
tag
(
'flt-canvas-container'
);
/// Specify the GPU resource cache limits.
void
setSkiaResourceCacheMaxBytes
(
int
bytes
)
{
_skiaCacheBytes
=
bytes
;
...
...
@@ -64,7 +81,7 @@ class Surface {
///
/// The given [size] is in physical pixels.
SurfaceFrame
acquireFrame
(
ui
.
Size
size
)
{
final
CkSurface
surface
=
acquireRenderSurface
(
size
);
final
CkSurface
surface
=
_createOrUpdateSurfaces
(
size
);
if
(
surface
.
context
!=
null
)
{
canvasKit
.
setCurrentContext
(
surface
.
context
!);
...
...
@@ -77,21 +94,16 @@ class Surface {
return
SurfaceFrame
(
surface
,
submitCallback
);
}
CkSurface
acquireRenderSurface
(
ui
.
Size
size
)
{
_createOrUpdateSurfaces
(
size
);
return
_surface
!;
}
void
addToScene
()
{
if
(!
_addedToScene
)
{
skiaSceneHost
!.
children
.
insert
(
0
,
htmlElement
!
);
skiaSceneHost
!.
children
.
insert
(
0
,
htmlElement
);
}
_addedToScene
=
true
;
}
ui
.
Size
?
_currentSize
;
void
_createOrUpdateSurfaces
(
ui
.
Size
size
)
{
CkSurface
_createOrUpdateSurfaces
(
ui
.
Size
size
)
{
if
(
size
.
isEmpty
)
{
throw
CanvasKitError
(
'Cannot create surfaces of empty size.'
);
}
...
...
@@ -99,11 +111,12 @@ class Surface {
// Check if the window is shrinking in size, and if so, don't allocate a
// new canvas as the previous canvas is big enough to fit everything.
final
ui
.
Size
?
previousSize
=
_currentSize
;
if
(
previousSize
!=
null
&&
if
(!
_forceNewContext
&&
previousSize
!=
null
&&
size
.
width
<=
previousSize
.
width
&&
size
.
height
<=
previousSize
.
height
)
{
// The existing surface is still reusable.
return
;
return
_surface
!
;
}
_currentSize
=
_currentSize
==
null
...
...
@@ -116,14 +129,17 @@ class Surface {
_surface
?.
dispose
();
_surface
=
null
;
htmlElement
?.
remove
();
htmlElement
=
null
;
_addedToScene
=
false
;
_surface
=
_wrapHtmlCanvas
(
_currentSize
!);
return
_surface
=
_wrapHtmlCanvas
(
_currentSize
!);
}
CkSurface
_wrapHtmlCanvas
(
ui
.
Size
physicalSize
)
{
// Clear the container, if it's not empty.
while
(
htmlElement
.
firstChild
!=
null
)
{
htmlElement
.
firstChild
!.
remove
();
}
// If `physicalSize` is not precise, use a slightly bigger canvas. This way
// we ensure that the rendred picture covers the entire browser window.
final
int
pixelWidth
=
physicalSize
.
width
.
ceil
();
...
...
@@ -146,9 +162,28 @@ class Surface {
..
width
=
'
${logicalWidth}
px'
..
height
=
'
${logicalHeight}
px'
;
htmlElement
=
htmlCanvas
;
if
(
webGLVersion
==
-
1
||
canvasKitForceCpuOnly
)
{
return
_makeSoftwareCanvasSurface
(
htmlCanvas
);
// When the browser tab using WebGL goes dormant the browser and/or OS may
// decide to clear GPU resources to let other tabs/programs use the GPU.
// When this happens, the browser sends the "webglcontextlost" event as a
// notification. When we receive this notification we force a new context.
//
// See also: https://www.khronos.org/webgl/wiki/HandlingContextLost
htmlCanvas
.
addEventListener
(
'webglcontextlost'
,
(
event
)
{
print
(
'Flutter: restoring WebGL context.'
);
_forceNewContext
=
true
;
// Force the framework to rerender the frame.
EnginePlatformDispatcher
.
instance
.
invokeOnMetricsChanged
();
event
.
stopPropagation
();
event
.
preventDefault
();
},
false
);
_forceNewContext
=
false
;
htmlElement
.
append
(
htmlCanvas
);
if
(
webGLVersion
==
-
1
)
{
return
_makeSoftwareCanvasSurface
(
htmlCanvas
,
'WebGL support not detected'
);
}
else
if
(
canvasKitForceCpuOnly
)
{
return
_makeSoftwareCanvasSurface
(
htmlCanvas
,
'CPU rendering forced by application'
);
}
else
{
// Try WebGL first.
final
int
glContext
=
canvasKit
.
GetWebGLContext
(
...
...
@@ -162,7 +197,7 @@ class Surface {
);
if
(
glContext
==
0
)
{
return
_makeSoftwareCanvasSurface
(
htmlCanvas
);
return
_makeSoftwareCanvasSurface
(
htmlCanvas
,
'Failed to initialize WebGL context'
);
}
_grContext
=
canvasKit
.
MakeGrContext
(
glContext
);
...
...
@@ -183,7 +218,7 @@ class Surface {
);
if
(
skSurface
==
null
)
{
return
_makeSoftwareCanvasSurface
(
htmlCanvas
);
return
_makeSoftwareCanvasSurface
(
htmlCanvas
,
'Failed to initialize WebGL surface'
);
}
return
CkSurface
(
skSurface
,
_grContext
,
glContext
);
...
...
@@ -192,9 +227,11 @@ class Surface {
static
bool
_didWarnAboutWebGlInitializationFailure
=
false
;
CkSurface
_makeSoftwareCanvasSurface
(
html
.
CanvasElement
htmlCanvas
)
{
CkSurface
_makeSoftwareCanvasSurface
(
html
.
CanvasElement
htmlCanvas
,
String
reason
)
{
if
(!
_didWarnAboutWebGlInitializationFailure
)
{
html
.
window
.
console
.
warn
(
'WARNING: failed to initialize WebGL. Falling back to CPU-only rendering.'
);
html
.
window
.
console
.
warn
(
'WARNING: Falling back to CPU-only rendering.
$reason
.'
);
_didWarnAboutWebGlInitializationFailure
=
true
;
}
return
CkSurface
(
...
...
@@ -211,6 +248,11 @@ class Surface {
_surface
!.
flush
();
return
true
;
}
void
dispose
()
{
htmlElement
.
remove
();
_surface
?.
dispose
();
}
}
/// A Dart wrapper around Skia's CkSurface.
...
...
lib/web_ui/test/canvaskit/common.dart
浏览文件 @
3d4c021f
...
...
@@ -38,6 +38,7 @@ void setUpCanvasKitTest() {
tearDown
(()
{
testCollector
.
cleanUpAfterTest
();
debugResetBrowserSupportsFinalizationRegistry
();
OverlayCache
.
instance
.
debugClear
();
});
tearDownAll
(()
{
...
...
lib/web_ui/test/canvaskit/embedded_views_test.dart
浏览文件 @
3d4c021f
...
...
@@ -114,6 +114,110 @@ void testMain() {
'matrix3d(5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 515, 515, 0, 1)'
,
);
});
test
(
'renders overlays on top of platform views'
,
()
async
{
expect
(
OverlayCache
.
instance
.
debugLength
,
0
);
final
CkPicture
testPicture
=
paintPicture
(
ui
.
Rect
.
fromLTRB
(
0
,
0
,
10
,
10
),
(
CkCanvas
canvas
)
{
canvas
.
drawCircle
(
ui
.
Offset
(
5
,
5
),
5
,
CkPaint
());
}
);
// Initialize all platform views to be used in the test.
final
List
<
int
>
platformViewIds
=
<
int
>[];
for
(
int
i
=
0
;
i
<
OverlayCache
.
kDefaultCacheSize
*
2
;
i
++)
{
ui
.
platformViewRegistry
.
registerViewFactory
(
'test-platform-view'
,
(
viewId
)
=>
html
.
DivElement
()..
id
=
'view-
$i
'
,
);
await
_createPlatformView
(
i
,
'test-platform-view'
);
platformViewIds
.
add
(
i
);
}
final
EnginePlatformDispatcher
dispatcher
=
ui
.
window
.
platformDispatcher
as
EnginePlatformDispatcher
;
void
renderTestScene
({
required
int
viewCount
})
{
LayerSceneBuilder
sb
=
LayerSceneBuilder
();
sb
.
pushOffset
(
0
,
0
);
for
(
int
i
=
0
;
i
<
viewCount
;
i
++)
{
sb
.
addPicture
(
ui
.
Offset
.
zero
,
testPicture
);
sb
.
addPlatformView
(
i
,
width:
10
,
height:
10
);
}
dispatcher
.
rasterizer
!.
draw
(
sb
.
build
().
layerTree
);
}
int
countCanvases
()
{
return
domRenderer
.
sceneElement
!.
querySelectorAll
(
'canvas'
).
length
;
}
// Frame 1:
// Render: up to cache size platform views.
// Expect: main canvas plus platform view overlays; empty cache.
renderTestScene
(
viewCount:
OverlayCache
.
kDefaultCacheSize
);
expect
(
countCanvases
(),
OverlayCache
.
kDefaultCacheSize
+
1
);
expect
(
OverlayCache
.
instance
.
debugLength
,
0
);
// Frame 2:
// Render: zero platform views.
// Expect: main canvas, no overlays; overlays in the cache.
await
Future
<
void
>.
delayed
(
Duration
.
zero
);
renderTestScene
(
viewCount:
0
);
expect
(
countCanvases
(),
1
);
expect
(
OverlayCache
.
instance
.
debugLength
,
5
);
// Frame 3:
// Render: less than cache size platform views.
// Expect: overlays reused; cache shrinks.
await
Future
<
void
>.
delayed
(
Duration
.
zero
);
renderTestScene
(
viewCount:
OverlayCache
.
kDefaultCacheSize
-
2
);
expect
(
countCanvases
(),
OverlayCache
.
kDefaultCacheSize
-
1
);
expect
(
OverlayCache
.
instance
.
debugLength
,
2
);
// Frame 4:
// Render: more platform views than max cache size.
// Expect: cache empty (everything reused).
await
Future
<
void
>.
delayed
(
Duration
.
zero
);
renderTestScene
(
viewCount:
OverlayCache
.
kDefaultCacheSize
*
2
);
expect
(
countCanvases
(),
OverlayCache
.
kDefaultCacheSize
*
2
+
1
);
expect
(
OverlayCache
.
instance
.
debugLength
,
0
);
// Frame 5:
// Render: zero platform views.
// Expect: main canvas, no overlays; cache full but does not exceed limit.
await
Future
<
void
>.
delayed
(
Duration
.
zero
);
renderTestScene
(
viewCount:
0
);
expect
(
countCanvases
(),
1
);
expect
(
OverlayCache
.
instance
.
debugLength
,
5
);
// Frame 6:
// Render: deleted platform views.
// Expect: error.
for
(
final
int
id
in
platformViewIds
)
{
final
codec
=
StandardMethodCodec
();
final
Completer
<
void
>
completer
=
Completer
<
void
>();
ui
.
window
.
sendPlatformMessage
(
'flutter/platform_views'
,
codec
.
encodeMethodCall
(
MethodCall
(
'dispose'
,
id
,
)),
completer
.
complete
,
);
await
completer
.
future
;
}
try
{
renderTestScene
(
viewCount:
platformViewIds
.
length
);
fail
(
'Expected to throw'
);
}
on
AssertionError
catch
(
error
)
{
expect
(
error
.
toString
(),
'Assertion failed: "Cannot render platform view 0. It has not been created, or it has been deleted."'
,
);
}
});
// TODO: https://github.com/flutter/flutter/issues/60040
},
skip:
isIosSafari
);
}
...
...
lib/web_ui/test/canvaskit/surface_test.dart
浏览文件 @
3d4c021f
...
...
@@ -3,6 +3,8 @@
// found in the LICENSE file.
// @dart = 2.12
import
'dart:html'
as
html
;
import
'package:test/bootstrap/browser.dart'
;
import
'package:test/test.dart'
;
import
'package:ui/src/engine.dart'
;
...
...
@@ -20,19 +22,19 @@ void testMain() {
test
(
'Surface allocates canvases efficiently'
,
()
{
final
Surface
surface
=
Surface
(
HtmlViewEmbedder
());
final
CkSurface
original
=
surface
.
acquire
RenderSurface
(
ui
.
Size
(
9
,
19
))
;
final
CkSurface
original
=
surface
.
acquire
Frame
(
ui
.
Size
(
9
,
19
)).
skiaSurface
;
// Expect exact requested dimensions.
expect
(
original
.
width
(),
9
);
expect
(
original
.
height
(),
19
);
// Shrinking reuses the existing surface straight-up.
final
CkSurface
shrunk
=
surface
.
acquire
RenderSurface
(
ui
.
Size
(
5
,
15
))
;
final
CkSurface
shrunk
=
surface
.
acquire
Frame
(
ui
.
Size
(
5
,
15
)).
skiaSurface
;
expect
(
shrunk
,
same
(
original
));
// The first increase will allocate a new surface, but will overallocate
// by 40% to accommodate future increases.
final
CkSurface
firstIncrease
=
surface
.
acquire
RenderSurface
(
ui
.
Size
(
10
,
20
))
;
final
CkSurface
firstIncrease
=
surface
.
acquire
Frame
(
ui
.
Size
(
10
,
20
)).
skiaSurface
;
expect
(
firstIncrease
,
isNot
(
same
(
original
)));
// Expect overallocated dimensions
...
...
@@ -40,11 +42,11 @@ void testMain() {
expect
(
firstIncrease
.
height
(),
28
);
// Subsequent increases within 40% reuse the old surface.
final
CkSurface
secondIncrease
=
surface
.
acquire
RenderSurface
(
ui
.
Size
(
11
,
22
))
;
final
CkSurface
secondIncrease
=
surface
.
acquire
Frame
(
ui
.
Size
(
11
,
22
)).
skiaSurface
;
expect
(
secondIncrease
,
same
(
firstIncrease
));
// Increases beyond the 40% limit will cause a new allocation.
final
CkSurface
huge
=
surface
.
acquire
RenderSurface
(
ui
.
Size
(
20
,
40
))
;
final
CkSurface
huge
=
surface
.
acquire
Frame
(
ui
.
Size
(
20
,
40
)).
skiaSurface
;
expect
(
huge
,
isNot
(
same
(
firstIncrease
)));
// Also over-allocated
...
...
@@ -52,8 +54,39 @@ void testMain() {
expect
(
huge
.
height
(),
56
);
// Shrink again. Reuse the last allocated surface.
final
CkSurface
shrunk2
=
surface
.
acquire
RenderSurface
(
ui
.
Size
(
5
,
15
))
;
final
CkSurface
shrunk2
=
surface
.
acquire
Frame
(
ui
.
Size
(
5
,
15
)).
skiaSurface
;
expect
(
shrunk2
,
same
(
huge
));
});
test
(
'Surface creates new context when WebGL context is lost'
,
()
async
{
final
Surface
surface
=
Surface
(
HtmlViewEmbedder
());
expect
(
surface
.
debugForceNewContext
,
isTrue
);
final
CkSurface
before
=
surface
.
acquireFrame
(
ui
.
Size
(
9
,
19
)).
skiaSurface
;
expect
(
surface
.
debugForceNewContext
,
isFalse
);
// Pump a timer to flush any microtasks.
await
Future
<
void
>.
delayed
(
Duration
.
zero
);
final
CkSurface
afterAcquireFrame
=
surface
.
acquireFrame
(
ui
.
Size
(
9
,
19
)).
skiaSurface
;
// Existing context is reused.
expect
(
afterAcquireFrame
,
same
(
before
));
// Emulate WebGL context loss.
final
html
.
CanvasElement
canvas
=
surface
.
htmlElement
.
children
.
single
as
html
.
CanvasElement
;
final
dynamic
ctx
=
canvas
.
getContext
(
'webgl2'
);
final
dynamic
loseContextExtension
=
ctx
.
getExtension
(
'WEBGL_lose_context'
);
loseContextExtension
.
loseContext
();
// Pump a timer to allow the "lose context" event to propagate.
await
Future
<
void
>.
delayed
(
Duration
.
zero
);
expect
(
surface
.
debugForceNewContext
,
isTrue
);
final
CkSurface
afterContextLost
=
surface
.
acquireFrame
(
ui
.
Size
(
9
,
19
)).
skiaSurface
;
// A new cotext is created.
expect
(
afterContextLost
,
isNot
(
same
(
before
)));
},
// Firefox doesn't have the WEBGL_lose_context extension.
skip:
isFirefox
||
isIosSafari
,
);
},
skip:
isIosSafari
);
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录