Browse Source

Added optimized Blit method for render layers

pull/4794/head
Nikita Tsukanov 5 years ago
parent
commit
7c250bba7f
  1. 9
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  2. 6
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  3. 11
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  4. 8
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  5. 2
      src/Avalonia.Visuals/Rendering/RenderLayer.cs
  6. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  7. 2
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  8. 20
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  9. 37
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  10. 2
      src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs
  11. 2
      src/Windows/Avalonia.Direct2D1/ILayerFactory.cs
  12. 4
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  13. 11
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
  14. 2
      src/Windows/Avalonia.Direct2D1/RenderTarget.cs
  15. 2
      src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs
  16. 2
      tests/Avalonia.UnitTests/TestRoot.cs

9
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -238,7 +238,7 @@ namespace Avalonia.Headless
}
}
class HeadlessBitmapStub : IBitmapImpl, IRenderTargetBitmapImpl, IWriteableBitmapImpl
class HeadlessBitmapStub : IBitmapImpl, IDrawingContextLayerImpl, IWriteableBitmapImpl
{
public Size Size { get; }
@ -267,6 +267,11 @@ namespace Avalonia.Headless
return new HeadlessDrawingContextStub();
}
public void Blit(IDrawingContextImpl context)
{
}
public Vector Dpi { get; }
public PixelSize PixelSize { get; }
public int Version { get; set; }
@ -307,7 +312,7 @@ namespace Avalonia.Headless
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return new HeadlessBitmapStub(size, new Vector(96, 96));
}

6
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -9,7 +9,11 @@ TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the imple
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IDrawingContextLayerImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
Total Issues: 13
MembersMustExist : Member 'public Avalonia.Utilities.IRef<Avalonia.Platform.IRenderTargetBitmapImpl> Avalonia.Rendering.RenderLayer.Bitmap.get()' does not exist in the implementation but it does exist in the contract.
Total Issues: 17

11
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -99,7 +99,7 @@ namespace Avalonia.Platform
/// has to do a format conversion each time a standard render target bitmap is rendered,
/// but a layer created via this method has no such overhead.
/// </remarks>
IRenderTargetBitmapImpl CreateLayer(Size size);
IDrawingContextLayerImpl CreateLayer(Size size);
/// <summary>
/// Pushes a clip rectangle.
@ -156,4 +156,13 @@ namespace Avalonia.Platform
/// <param name="custom">Custom draw operation</param>
void Custom(ICustomDrawOperation custom);
}
public interface IDrawingContextLayerImpl : IRenderTargetBitmapImpl
{
/// <summary>
/// Does optimized blit with Src blend mode
/// </summary>
/// <param name="context"></param>
void Blit(IDrawingContextImpl context);
}
}

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

@ -489,6 +489,7 @@ namespace Avalonia.Rendering
var clientRect = new Rect(scene.Size);
var firstLayer = true;
foreach (var layer in scene.Layers)
{
var bitmap = Layers[layer.LayerRoot].Bitmap;
@ -501,7 +502,10 @@ namespace Avalonia.Rendering
if (layer.OpacityMask == null)
{
context.DrawBitmap(bitmap, layer.Opacity, sourceRect, clientRect);
if (firstLayer)
bitmap.Item.Blit(context);
else
context.DrawBitmap(bitmap, layer.Opacity, sourceRect, clientRect);
}
else
{
@ -512,6 +516,8 @@ namespace Avalonia.Rendering
{
context.PopGeometryClip();
}
firstLayer = false;
}
if (_overlay != null)

2
src/Avalonia.Visuals/Rendering/RenderLayer.cs

@ -20,7 +20,7 @@ namespace Avalonia.Rendering
IsEmpty = true;
}
public IRef<IRenderTargetBitmapImpl> Bitmap { get; private set; }
public IRef<IDrawingContextLayerImpl> Bitmap { get; private set; }
public bool IsEmpty { get; set; }
public double Scaling { get; private set; }
public Size Size { get; private set; }

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

@ -218,7 +218,7 @@ namespace Avalonia.Rendering.SceneGraph
++_drawOperationindex;
}
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
throw new NotSupportedException("Creating layers on a deferred drawing context not supported");
}

2
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -422,7 +422,7 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return CreateRenderTarget( size);
}

20
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using Avalonia.Logging;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
using Avalonia.OpenGL.Surfaces;
@ -10,6 +12,7 @@ namespace Avalonia.Skia
{
private GRContext _grContext;
private IGlContext _glContext;
private bool? _canCreateSurfaces;
public GlSkiaGpu(IPlatformOpenGlInterface openGl, long? maxResourceBytes)
{
@ -45,7 +48,22 @@ namespace Avalonia.Skia
public ISkiaSurface TryCreateSurface(PixelSize size)
{
return new FboSkiaSurface(_grContext, _glContext, size);
size = new PixelSize(Math.Max(size.Width, 1), Math.Max(size.Height, 1));
if (_canCreateSurfaces == false)
return null;
try
{
var surface = new FboSkiaSurface(_grContext, _glContext, size);
_canCreateSurfaces = true;
return surface;
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")
?.Log(this, "Unable to create a Skia-compatible FBO manually");
_canCreateSurfaces ??= false;
return null;
}
}
public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi) => new GlOpenGlBitmapImpl(_glContext, size, dpi);

37
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Reactive.Disposables;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Skia.Helpers;
@ -11,7 +12,7 @@ namespace Avalonia.Skia
/// <summary>
/// Skia render target that writes to a surface.
/// </summary>
internal class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
internal class SurfaceRenderTarget : IDrawingContextLayerImpl, IDrawableBitmapImpl
{
private readonly ISkiaSurface _surface;
private readonly SKCanvas _canvas;
@ -133,23 +134,33 @@ namespace Avalonia.Skia
}
}
/// <inheritdoc />
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
public void Blit(IDrawingContextImpl contextImpl)
{
if (sourceRect.Left == 0 && sourceRect.Top == 0 && sourceRect.Size == destRect.Size)
var context = (DrawingContextImpl)contextImpl;
if (_surface.CanBlit)
{
_surface.Surface.Canvas.Flush();
if (context.Canvas.TotalMatrix.IsIdentity && _surface.CanBlit && destRect.Top == 0 &&
destRect.Left == 0)
_surface.Blit();
else
_surface.Surface.Draw(context.Canvas, destRect.Left, destRect.Top, paint);
// This should set the render target as the current FBO
context.Canvas.Clear();
context.Canvas.Flush();
_surface.Blit();
}
else
using (var image = SnapshotImage())
{
context.Canvas.DrawImage(image, sourceRect, destRect, paint);
}
{
var oldMatrix = context.Canvas.TotalMatrix;
context.Canvas.ResetMatrix();
_surface.Surface.Draw(context.Canvas, 0, 0, null);
context.Canvas.SetMatrix(oldMatrix);
}
}
/// <inheritdoc />
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
{
using var image = SnapshotImage();
context.Canvas.DrawImage(image, sourceRect, destRect, paint);
}
/// <summary>

2
src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs

@ -38,7 +38,7 @@ namespace Avalonia.Direct2D1
});
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
var renderTarget = _externalRenderTargetProvider.GetOrCreateRenderTarget();
return D2DRenderTargetBitmapImpl.CreateCompatible(renderTarget, size);

2
src/Windows/Avalonia.Direct2D1/ILayerFactory.cs

@ -4,6 +4,6 @@ namespace Avalonia.Direct2D1
{
public interface ILayerFactory
{
IRenderTargetBitmapImpl CreateLayer(Size size);
IDrawingContextLayerImpl CreateLayer(Size size);
}
}

4
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -334,7 +334,7 @@ namespace Avalonia.Direct2D1.Media
}
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
if (_layerFactory != null)
{
@ -345,7 +345,7 @@ namespace Avalonia.Direct2D1.Media
var platform = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
var dpi = new Vector(_deviceContext.DotsPerInch.Width, _deviceContext.DotsPerInch.Height);
var pixelSize = PixelSize.FromSizeWithDpi(size, dpi);
return platform.CreateRenderTargetBitmap(pixelSize, dpi);
return (IDrawingContextLayerImpl)platform.CreateRenderTargetBitmap(pixelSize, dpi);
}
}

11
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs

@ -8,7 +8,7 @@ using D2DBitmap = SharpDX.Direct2D1.Bitmap;
namespace Avalonia.Direct2D1.Media.Imaging
{
public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IRenderTargetBitmapImpl, ILayerFactory
public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IDrawingContextLayerImpl, ILayerFactory
{
private readonly BitmapRenderTarget _renderTarget;
@ -34,7 +34,14 @@ namespace Avalonia.Direct2D1.Media.Imaging
return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget, null, () => Version++);
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public void Blit(IDrawingContextImpl context)
{
var rc = new Rect(0, 0, PixelSize.Width, PixelSize.Height);
context.DrawBitmap(RefCountable.CreateUnownedNotClonable(this),
1, rc, rc);
}
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return CreateCompatible(_renderTarget, size);
}

2
src/Windows/Avalonia.Direct2D1/RenderTarget.cs

@ -30,7 +30,7 @@ namespace Avalonia.Direct2D1
return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget);
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return D2DRenderTargetBitmapImpl.CreateCompatible(_renderTarget, size);
}

2
src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs

@ -35,7 +35,7 @@ namespace Avalonia.Direct2D1
return new DrawingContextImpl(visualBrushRenderer, this, _deviceContext, _swapChain);
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
if (_deviceContext == null)
{

2
tests/Avalonia.UnitTests/TestRoot.cs

@ -72,7 +72,7 @@ namespace Avalonia.UnitTests
dc.Setup(x => x.CreateLayer(It.IsAny<Size>())).Returns(() =>
{
var layerDc = new Mock<IDrawingContextImpl>();
var layer = new Mock<IRenderTargetBitmapImpl>();
var layer = new Mock<IDrawingContextLayerImpl>();
layer.Setup(x => x.CreateDrawingContext(It.IsAny<IVisualBrushRenderer>())).Returns(layerDc.Object);
return layer.Object;
});

Loading…
Cancel
Save