|
|
|
@ -205,99 +205,105 @@ namespace Avalonia.Rendering |
|
|
|
|
|
|
|
internal void UnitTestUpdateScene() => UpdateScene(); |
|
|
|
|
|
|
|
internal void UnitTestRender() => Render(_scene.Item, false, false); |
|
|
|
internal void UnitTestRender() => Render(false); |
|
|
|
|
|
|
|
private void Render(bool forceComposite) |
|
|
|
{ |
|
|
|
using (var l = _lock.TryLock()) |
|
|
|
if (l != null) |
|
|
|
{ |
|
|
|
bool reRun = false; |
|
|
|
do |
|
|
|
{ |
|
|
|
IRef<Scene> scene; |
|
|
|
lock (_sceneLock) |
|
|
|
scene = _scene?.Clone(); |
|
|
|
using (scene) |
|
|
|
reRun = Render(scene?.Item, forceComposite, reRun); |
|
|
|
} while (reRun); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private bool Render(Scene scene, bool forceComposite, bool reUpdating) |
|
|
|
{ |
|
|
|
bool renderOverlay = DrawDirtyRects || DrawFps; |
|
|
|
bool composite = false; |
|
|
|
|
|
|
|
if (RenderTarget == null) |
|
|
|
{ |
|
|
|
RenderTarget = ((IRenderRoot)_root).CreateRenderTarget(); |
|
|
|
} |
|
|
|
|
|
|
|
if (renderOverlay) |
|
|
|
{ |
|
|
|
_dirtyRectsDisplay.Tick(); |
|
|
|
} |
|
|
|
if (l == null) |
|
|
|
return; |
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
if (scene != null && scene.Size != Size.Empty) |
|
|
|
IDrawingContextImpl context = null; |
|
|
|
try |
|
|
|
{ |
|
|
|
IDrawingContextImpl context = null; |
|
|
|
|
|
|
|
if (scene.Generation != _lastSceneId) |
|
|
|
try |
|
|
|
{ |
|
|
|
context = RenderTarget.CreateDrawingContext(this); |
|
|
|
Layers.Update(scene, context); |
|
|
|
|
|
|
|
RenderToLayers(scene); |
|
|
|
|
|
|
|
if (DebugFramesPath != null) |
|
|
|
IDrawingContextImpl GetContext() |
|
|
|
{ |
|
|
|
SaveDebugFrames(scene.Generation); |
|
|
|
} |
|
|
|
if (context != null) |
|
|
|
return context; |
|
|
|
if (RenderTarget == null) |
|
|
|
RenderTarget = ((IRenderRoot)_root).CreateRenderTarget(); |
|
|
|
return context = RenderTarget.CreateDrawingContext(this); |
|
|
|
|
|
|
|
lock (_sceneLock) |
|
|
|
_lastSceneId = scene.Generation; |
|
|
|
} |
|
|
|
|
|
|
|
// We have consumed the previously available scene, but there might be some dirty
|
|
|
|
// rects since the last update. *If* we are on UI thread, we can force immediate scene
|
|
|
|
// rebuild before rendering anything on-screen
|
|
|
|
// By returning true we indicate that this method should be called again
|
|
|
|
if (!reUpdating && Dispatcher.UIThread.CheckAccess() && NeedsUpdate) |
|
|
|
var (scene, updated) = UpdateRenderLayersAndConsumeSceneIfNeeded(GetContext); |
|
|
|
using (scene) |
|
|
|
{ |
|
|
|
UpdateScene(); |
|
|
|
return true; |
|
|
|
var overlay = DrawDirtyRects || DrawFps; |
|
|
|
if (DrawDirtyRects) |
|
|
|
_dirtyRectsDisplay.Tick(); |
|
|
|
if (overlay) |
|
|
|
RenderOverlay(scene.Item, GetContext()); |
|
|
|
if (updated || forceComposite || overlay) |
|
|
|
RenderComposite(scene.Item, GetContext()); |
|
|
|
} |
|
|
|
|
|
|
|
composite = true; |
|
|
|
} |
|
|
|
|
|
|
|
if (renderOverlay) |
|
|
|
finally |
|
|
|
{ |
|
|
|
context = context ?? RenderTarget.CreateDrawingContext(this); |
|
|
|
RenderOverlay(scene, context); |
|
|
|
RenderComposite(scene, context); |
|
|
|
context?.Dispose(); |
|
|
|
} |
|
|
|
else if (composite || forceComposite) |
|
|
|
} |
|
|
|
catch (RenderTargetCorruptedException ex) |
|
|
|
{ |
|
|
|
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex); |
|
|
|
RenderTarget?.Dispose(); |
|
|
|
RenderTarget = null; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private (IRef<Scene> scene, bool updated) UpdateRenderLayersAndConsumeSceneIfNeeded(Func<IDrawingContextImpl> contextFactory, |
|
|
|
bool recursiveCall = false) |
|
|
|
{ |
|
|
|
IRef<Scene> sceneRef; |
|
|
|
lock (_sceneLock) |
|
|
|
sceneRef = _scene?.Clone(); |
|
|
|
if (sceneRef == null) |
|
|
|
return (null, false); |
|
|
|
using (sceneRef) |
|
|
|
{ |
|
|
|
var scene = sceneRef.Item; |
|
|
|
if (scene.Generation != _lastSceneId) |
|
|
|
{ |
|
|
|
var context = contextFactory(); |
|
|
|
Layers.Update(scene, context); |
|
|
|
|
|
|
|
RenderToLayers(scene); |
|
|
|
|
|
|
|
if (DebugFramesPath != null) |
|
|
|
{ |
|
|
|
context = context ?? RenderTarget.CreateDrawingContext(this); |
|
|
|
RenderComposite(scene, context); |
|
|
|
SaveDebugFrames(scene.Generation); |
|
|
|
} |
|
|
|
|
|
|
|
context?.Dispose(); |
|
|
|
lock (_sceneLock) |
|
|
|
_lastSceneId = scene.Generation; |
|
|
|
|
|
|
|
|
|
|
|
// We have consumed the previously available scene, but there might be some dirty
|
|
|
|
// rects since the last update. *If* we are on UI thread, we can force immediate scene
|
|
|
|
// rebuild before rendering anything on-screen
|
|
|
|
// We are calling the same method recursively here
|
|
|
|
if (!recursiveCall && Dispatcher.UIThread.CheckAccess() && NeedsUpdate) |
|
|
|
{ |
|
|
|
UpdateScene(); |
|
|
|
var (rs, _) = UpdateRenderLayersAndConsumeSceneIfNeeded(contextFactory, true); |
|
|
|
return (rs, true); |
|
|
|
} |
|
|
|
|
|
|
|
// Indicate that we have updated the layers
|
|
|
|
return (sceneRef.Clone(), true); |
|
|
|
} |
|
|
|
|
|
|
|
// Just return scene, layers weren't updated
|
|
|
|
return (sceneRef.Clone(), false); |
|
|
|
} |
|
|
|
catch (RenderTargetCorruptedException ex) |
|
|
|
{ |
|
|
|
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex); |
|
|
|
RenderTarget?.Dispose(); |
|
|
|
RenderTarget = null; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds) |
|
|
|
{ |
|
|
|
if (layer == null || node.LayerRoot == layer) |
|
|
|
|