Browse Source

Merge branch 'manual-fbo' into render-inside-win-ui-comp-tree

# Conflicts:
#	src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
tmp-winuicomp_with_manual_fbo
Dan Walmsley 5 years ago
parent
commit
9b0be060ae
  1. 13
      src/Avalonia.OpenGL/GlInterface.cs
  2. 12
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  3. 15
      src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
  4. 129
      src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
  5. 5
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  6. 7
      src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
  7. 2
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  8. 48
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

13
src/Avalonia.OpenGL/GlInterface.cs

@ -117,6 +117,19 @@ namespace Avalonia.OpenGL
public delegate int GlCheckFramebufferStatus(int target);
[GlEntryPoint("glCheckFramebufferStatus")]
public GlCheckFramebufferStatus CheckFramebufferStatus { get; }
public delegate void GlBlitFramebuffer(int srcX0,
int srcY0,
int srcX1,
int srcY1,
int dstX0,
int dstY0,
int dstX1,
int dstY1,
int mask,
int filter);
[GlMinVersionEntryPoint("glBlitFramebuffer", 3, 0)]
public GlBlitFramebuffer BlitFramebuffer { get; }
public delegate void GlGenRenderbuffers(int count, int[] res);
[GlEntryPoint("glGenRenderbuffers")]

12
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -31,6 +31,7 @@ namespace Avalonia.Skia
private bool _disposed;
private GRContext _grContext;
public GRContext GrContext => _grContext;
private ISkiaGpu _gpu;
private readonly SKPaint _strokePaint = new SKPaint();
private readonly SKPaint _fillPaint = new SKPaint();
private readonly SKPaint _boxShadowPaint = new SKPaint();
@ -65,6 +66,11 @@ namespace Avalonia.Skia
/// GPU-accelerated context (optional)
/// </summary>
public GRContext GrContext;
/// <summary>
/// Skia GPU provider context (optional)
/// </summary>
public ISkiaGpu Gpu;
}
/// <summary>
@ -79,6 +85,7 @@ namespace Avalonia.Skia
_disposables = disposables;
_canTextUseLcdRendering = !createInfo.DisableTextLcdRendering;
_grContext = createInfo.GrContext;
_gpu = createInfo.Gpu;
if (_grContext != null)
Monitor.Enter(_grContext);
Surface = createInfo.Surface;
@ -417,7 +424,7 @@ namespace Avalonia.Skia
/// <inheritdoc />
public IRenderTargetBitmapImpl CreateLayer(Size size)
{
return CreateRenderTarget(size);
return CreateRenderTarget( size);
}
/// <inheritdoc />
@ -925,7 +932,8 @@ namespace Avalonia.Skia
Dpi = _dpi,
Format = format,
DisableTextLcdRendering = !_canTextUseLcdRendering,
GrContext = _grContext
GrContext = _grContext,
Gpu = _gpu
};
return new SurfaceRenderTarget(createInfo);

15
src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Avalonia.OpenGL.Imaging;
using SkiaSharp;
@ -15,6 +16,20 @@ namespace Avalonia.Skia
/// <param name="surfaces">Surfaces.</param>
/// <returns>Created render target or <see langword="null"/> if it fails.</returns>
ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces);
/// <summary>
/// Creates an offscreen render target surface
/// </summary>
/// <param name="size">size in pixels</param>
ISkiaSurface TryCreateSurface(PixelSize size);
}
public interface ISkiaSurface : IDisposable
{
SKSurface Surface { get; }
bool CanBlit { get; }
void Blit();
}
public interface IOpenGlAwareSkiaGpu : ISkiaGpu

129
src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs

@ -0,0 +1,129 @@
using System;
using Avalonia.OpenGL;
using SkiaSharp;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia
{
public class FboSkiaSurface : ISkiaSurface
{
private readonly GRContext _grContext;
private readonly IGlContext _glContext;
private readonly PixelSize _pixelSize;
private int _fbo;
private int _depthStencil;
private int _texture;
private static readonly bool[] TrueFalse = new[] { true, false };
public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize)
{
_grContext = grContext;
_glContext = glContext;
_pixelSize = pixelSize;
var InternalFormat = glContext.Version.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8;
var gl = glContext.GlInterface;
// Save old bindings
gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFbo);
gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderbuffer);
gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture);
var arr = new int[2];
// Generate FBO
gl.GenFramebuffers(1, arr);
_fbo = arr[0];
gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo);
// Create a texture to render into
gl.GenTextures(1, arr);
_texture = arr[0];
gl.BindTexture(GL_TEXTURE_2D, _texture);
gl.TexImage2D(GL_TEXTURE_2D, 0,
InternalFormat, pixelSize.Width, pixelSize.Height,
0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
var success = false;
foreach (var useStencil8 in TrueFalse)
{
gl.GenRenderbuffers(1, arr);
_depthStencil = arr[0];
gl.BindRenderbuffer(GL_RENDERBUFFER, _depthStencil);
if (useStencil8)
{
gl.RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, pixelSize.Width, pixelSize.Height);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthStencil);
}
else
{
gl.RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, pixelSize.Width, pixelSize.Height);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthStencil);
}
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE)
{
success = true;
break;
}
else
{
gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderbuffer);
gl.DeleteRenderbuffers(1, arr);
}
}
gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderbuffer);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo);
if (!success)
{
arr[0] = _fbo;
gl.DeleteFramebuffers(1, arr);
arr[0] = _texture;
gl.DeleteTextures(1, arr);
throw new OpenGlException("Unable to create FBO with stencil");
}
var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 0,
new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat()));
Surface = SKSurface.Create(_grContext, target,
GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
CanBlit = gl.BlitFramebuffer != null;
}
public void Dispose()
{
using (_glContext.EnsureCurrent())
{
Surface?.Dispose();
Surface = null;
var gl = _glContext.GlInterface;
if (_fbo != 0)
{
gl.DeleteFramebuffers(1, new[] { _fbo });
gl.DeleteTextures(1, new[] { _texture });
gl.DeleteRenderbuffers(1, new[] { _depthStencil });
_fbo = _texture = _depthStencil = 0;
}
}
}
public SKSurface Surface { get; private set; }
public bool CanBlit { get; }
public void Blit()
{
var gl = _glContext.GlInterface;
gl.GetIntegerv(GL_READ_FRAMEBUFFER_BINDING, out var oldRead);
gl.BindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
gl.BlitFramebuffer(0, 0, _pixelSize.Width, _pixelSize.Height, 0, 0, _pixelSize.Width, _pixelSize.Height,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
gl.BindFramebuffer(GL_READ_FRAMEBUFFER, oldRead);
}
}
}

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

@ -43,6 +43,11 @@ namespace Avalonia.Skia
return null;
}
public ISkiaSurface TryCreateSurface(PixelSize size)
{
return new FboSkiaSurface(_grContext, _glContext, size);
}
public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi) => new GlOpenGlBitmapImpl(_glContext, size, dpi);
}
}

7
src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs

@ -8,10 +8,12 @@ namespace Avalonia.Skia
/// </summary>
internal class SkiaGpuRenderTarget : IRenderTargetWithCorruptionInfo
{
private readonly ISkiaGpu _skiaGpu;
private readonly ISkiaGpuRenderTarget _renderTarget;
public SkiaGpuRenderTarget(ISkiaGpuRenderTarget renderTarget)
public SkiaGpuRenderTarget(ISkiaGpu skiaGpu, ISkiaGpuRenderTarget renderTarget)
{
_skiaGpu = skiaGpu;
_renderTarget = renderTarget;
}
@ -30,7 +32,8 @@ namespace Avalonia.Skia
Surface = session.SkSurface,
Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor,
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = true
DisableTextLcdRendering = true,
Gpu = _skiaGpu
};
return new DrawingContextImpl(nfo, session);

2
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -138,7 +138,7 @@ namespace Avalonia.Skia
var gpuRenderTarget = _skiaGpu?.TryCreateRenderTarget(surfaces);
if (gpuRenderTarget != null)
{
return new SkiaGpuRenderTarget(gpuRenderTarget);
return new SkiaGpuRenderTarget(_skiaGpu, gpuRenderTarget);
}
foreach (var surface in surfaces)

48
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -13,10 +13,31 @@ namespace Avalonia.Skia
/// </summary>
internal class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
{
private readonly SKSurface _surface;
private readonly ISkiaSurface _surface;
private readonly SKCanvas _canvas;
private readonly bool _disableLcdRendering;
private readonly GRContext _grContext;
private readonly ISkiaGpu _gpu;
class SkiaSurfaceWrapper : ISkiaSurface
{
public SKSurface Surface { get; private set; }
public SKSurface ReadSurface { get; private set; }
public bool CanBlit => false;
public void Blit() => throw new NotSupportedException();
public SkiaSurfaceWrapper(SKSurface surface)
{
Surface = ReadSurface = surface;
}
public void Dispose()
{
Surface?.Dispose();
Surface = null;
ReadSurface = null;
}
}
/// <summary>
/// Create new surface render target.
@ -29,9 +50,14 @@ namespace Avalonia.Skia
_disableLcdRendering = createInfo.DisableTextLcdRendering;
_grContext = createInfo.GrContext;
_surface = CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, createInfo.Format);
_gpu = createInfo.Gpu;
_canvas = _surface?.Canvas;
_surface = _gpu?.TryCreateSurface(PixelSize);
if (_surface == null)
_surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height,
createInfo.Format));
_canvas = _surface?.Surface.Canvas;
if (_surface == null || _canvas == null)
{
@ -70,11 +96,12 @@ namespace Avalonia.Skia
var createInfo = new DrawingContextImpl.CreateInfo
{
Surface = _surface,
Surface = _surface.Surface,
Dpi = Dpi,
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = _disableLcdRendering,
GrContext = _grContext
GrContext = _grContext,
Gpu = _gpu
};
return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++));
@ -111,7 +138,12 @@ namespace Avalonia.Skia
{
using (var image = SnapshotImage())
{
context.Canvas.DrawImage(image, sourceRect, destRect, paint);
_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);
}
}
@ -121,7 +153,7 @@ namespace Avalonia.Skia
/// <returns>Image snapshot.</returns>
public SKImage SnapshotImage()
{
return _surface.Snapshot();
return _surface.Surface.Snapshot();
}
/// <summary>
@ -172,6 +204,8 @@ namespace Avalonia.Skia
/// GPU-accelerated context (optional)
/// </summary>
public GRContext GrContext;
public ISkiaGpu Gpu;
}
}
}

Loading…
Cancel
Save