Browse Source

Add ref-counting for the scene instance in DeferredRenderer to ensure that we don't dispose a scene while it is being rendered.

Also rename Scene.Clone to Scene.CloneScene to prevent confusion between IRef<Scene>.Clone and Scene.Clone.
pull/1365/head
Jeremy Koritzinsky 9 years ago
parent
commit
8817910549
  1. 26
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  2. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  3. 22
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
  4. 6
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs
  5. 2
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs

26
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -28,7 +28,7 @@ namespace Avalonia.Rendering
private readonly ISceneBuilder _sceneBuilder; private readonly ISceneBuilder _sceneBuilder;
private bool _running; private bool _running;
private Scene _scene; private volatile IRef<Scene> _scene;
private DirtyVisuals _dirty; private DirtyVisuals _dirty;
private IRef<IRenderTargetBitmapImpl> _overlay; private IRef<IRenderTargetBitmapImpl> _overlay;
private bool _updateQueued; private bool _updateQueued;
@ -128,7 +128,7 @@ namespace Avalonia.Rendering
UpdateScene(); UpdateScene();
} }
return _scene?.HitTest(p, root, filter) ?? Enumerable.Empty<IVisual>(); return _scene?.Item.HitTest(p, root, filter) ?? Enumerable.Empty<IVisual>();
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -180,7 +180,7 @@ namespace Avalonia.Rendering
internal void UnitTestUpdateScene() => UpdateScene(); internal void UnitTestUpdateScene() => UpdateScene();
internal void UnitTestRender() => Render(_scene); internal void UnitTestRender() => Render(_scene.Item);
private void Render(Scene scene) private void Render(Scene scene)
{ {
@ -381,7 +381,8 @@ namespace Avalonia.Rendering
{ {
if (_root.IsVisible) if (_root.IsVisible)
{ {
var scene = _scene?.Clone() ?? new Scene(_root); var sceneRef = RefCountable.Create(_scene?.Item.CloneScene() ?? new Scene(_root));
var scene = sceneRef.Item;
if (_dirty == null) if (_dirty == null)
{ {
@ -396,7 +397,7 @@ namespace Avalonia.Rendering
} }
} }
var oldScene = Interlocked.Exchange(ref _scene, scene); var oldScene = Interlocked.Exchange(ref _scene, sceneRef);
oldScene?.Dispose(); oldScene?.Dispose();
_dirty.Clear(); _dirty.Clear();
@ -426,9 +427,18 @@ namespace Avalonia.Rendering
_dispatcher.Post(UpdateScene, DispatcherPriority.Render); _dispatcher.Post(UpdateScene, DispatcherPriority.Render);
} }
Scene scene = null; var scene = _scene?.Clone();
Interlocked.Exchange(ref scene, _scene); if (scene == null)
Render(scene); {
Render(null);
}
else
{
using (scene)
{
Render(scene.Item);
}
}
} }
catch { } catch { }
finally finally

2
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -82,7 +82,7 @@ namespace Avalonia.Rendering.SceneGraph
/// Clones the scene. /// Clones the scene.
/// </summary> /// </summary>
/// <returns>The cloned scene.</returns> /// <returns>The cloned scene.</returns>
public Scene Clone() public Scene CloneScene()
{ {
var index = new Dictionary<IVisual, IVisualNode>(); var index = new Dictionary<IVisual, IVisualNode>();
var root = Clone((VisualNode)Root, null, index); var root = Clone((VisualNode)Root, null, index);

22
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@ -99,7 +99,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(new Rect(10, 20, 160, 240), canvasNode.ClipBounds); Assert.Equal(new Rect(10, 20, 160, 240), canvasNode.ClipBounds);
// Initial ClipBounds are correct, make sure they're still correct after updating canvas. // Initial ClipBounds are correct, make sure they're still correct after updating canvas.
result = result.Clone(); result = result.CloneScene();
Assert.True(sceneBuilder.Update(result, canvas)); Assert.True(sceneBuilder.Update(result, canvas));
canvasNode = result.FindNode(canvas); canvasNode = result.FindNode(canvas);
@ -197,7 +197,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
canvas.Arrange(new Rect(tree.DesiredSize)); canvas.Arrange(new Rect(tree.DesiredSize));
// Initial ClipBounds are correct, make sure they're still correct after updating canvas. // Initial ClipBounds are correct, make sure they're still correct after updating canvas.
scene = scene.Clone(); scene = scene.CloneScene();
Assert.True(sceneBuilder.Update(scene, canvas)); Assert.True(sceneBuilder.Update(scene, canvas));
borderNode = scene.FindNode(border); borderNode = scene.FindNode(border);
@ -309,7 +309,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
var borderNode = scene.FindNode(border); var borderNode = scene.FindNode(border);
Assert.Equal(expectedTransform, borderNode.Transform); Assert.Equal(expectedTransform, borderNode.Transform);
scene = scene.Clone(); scene = scene.CloneScene();
Assert.True(sceneBuilder.Update(scene, border)); Assert.True(sceneBuilder.Update(scene, border));
borderNode = scene.FindNode(border); borderNode = scene.FindNode(border);
@ -354,7 +354,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
border.Background = Brushes.Green; border.Background = Brushes.Green;
var result = initial.Clone(); var result = initial.CloneScene();
sceneBuilder.Update(result, border); sceneBuilder.Update(result, border);
var borderNode = (VisualNode)result.Root.Children[0]; var borderNode = (VisualNode)result.Root.Children[0];
@ -402,7 +402,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
sceneBuilder.UpdateAll(initial); sceneBuilder.UpdateAll(initial);
border.Child = decorator; border.Child = decorator;
var result = initial.Clone(); var result = initial.CloneScene();
Assert.True(sceneBuilder.Update(result, decorator)); Assert.True(sceneBuilder.Update(result, decorator));
@ -457,7 +457,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
sceneBuilder.UpdateAll(initial); sceneBuilder.UpdateAll(initial);
border.Child = null; border.Child = null;
var result = initial.Clone(); var result = initial.CloneScene();
Assert.True(sceneBuilder.Update(result, decorator)); Assert.True(sceneBuilder.Update(result, decorator));
Assert.False(sceneBuilder.Update(result, canvas)); Assert.False(sceneBuilder.Update(result, canvas));
@ -501,7 +501,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
sceneBuilder.UpdateAll(initial); sceneBuilder.UpdateAll(initial);
border.IsVisible = false; border.IsVisible = false;
var result = initial.Clone(); var result = initial.CloneScene();
Assert.True(sceneBuilder.Update(result, border)); Assert.True(sceneBuilder.Update(result, border));
Assert.False(sceneBuilder.Update(result, canvas)); Assert.False(sceneBuilder.Update(result, canvas));
@ -552,7 +552,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
decorator.Margin = new Thickness(0, 20, 0, 0); decorator.Margin = new Thickness(0, 20, 0, 0);
layout.ExecuteLayoutPass(); layout.ExecuteLayoutPass();
scene = scene.Clone(); scene = scene.CloneScene();
sceneBuilder.Update(scene, decorator); sceneBuilder.Update(scene, decorator);
borderNode = scene.FindNode(border); borderNode = scene.FindNode(border);
@ -600,7 +600,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
decorator.Margin = new Thickness(0, 20, 0, 0); decorator.Margin = new Thickness(0, 20, 0, 0);
layout.ExecuteLayoutPass(); layout.ExecuteLayoutPass();
scene = scene.Clone(); scene = scene.CloneScene();
sceneBuilder.Update(scene, decorator); sceneBuilder.Update(scene, decorator);
@ -640,7 +640,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(new Size(100, 100), scene.Size); Assert.Equal(new Size(100, 100), scene.Size);
tree.ClientSize = new Size(110, 120); tree.ClientSize = new Size(110, 120);
scene = scene.Clone(); scene = scene.CloneScene();
sceneBuilder.Update(scene, tree); sceneBuilder.Update(scene, tree);
Assert.Equal(new Size(110, 120), scene.Size); Assert.Equal(new Size(110, 120), scene.Size);
@ -735,7 +735,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
tree.Child = new Decorator(); tree.Child = new Decorator();
using (var result = scene.Clone()) using (var result = scene.CloneScene())
{ {
sceneBuilder.Update(result, img); sceneBuilder.Update(result, img);
scene.Dispose(); scene.Dispose();

6
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs

@ -62,7 +62,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Empty(scene.Layers.Select(x => x.LayerRoot).Except(new IVisual[] { tree, border })); Assert.Empty(scene.Layers.Select(x => x.LayerRoot).Except(new IVisual[] { tree, border }));
animation.OnCompleted(); animation.OnCompleted();
scene = scene.Clone(); scene = scene.CloneScene();
sceneBuilder.Update(scene, border); sceneBuilder.Update(scene, border);
@ -160,7 +160,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(3, scene.Layers.Count); Assert.Equal(3, scene.Layers.Count);
decorator.Child = null; decorator.Child = null;
scene = scene.Clone(); scene = scene.CloneScene();
sceneBuilder.Update(scene, border); sceneBuilder.Update(scene, border);
@ -210,7 +210,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(3, scene.Layers.Count); Assert.Equal(3, scene.Layers.Count);
border.IsVisible = false; border.IsVisible = false;
scene = scene.Clone(); scene = scene.CloneScene();
sceneBuilder.Update(scene, border); sceneBuilder.Update(scene, border);

2
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs

@ -25,7 +25,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
scene.Layers[tree].Dirty.Add(new Rect(0, 0, 100, 100)); scene.Layers[tree].Dirty.Add(new Rect(0, 0, 100, 100));
scene.Layers[decorator].Dirty.Add(new Rect(0, 0, 50, 100)); scene.Layers[decorator].Dirty.Add(new Rect(0, 0, 50, 100));
scene = scene.Clone(); scene = scene.CloneScene();
Assert.Equal(2, scene.Layers.Count()); Assert.Equal(2, scene.Layers.Count());
Assert.Empty(scene.Layers[0].Dirty); Assert.Empty(scene.Layers[0].Dirty);
Assert.Empty(scene.Layers[1].Dirty); Assert.Empty(scene.Layers[1].Dirty);

Loading…
Cancel
Save