Browse Source

Manually setup FBOs for layers

Nikita Tsukanov 6 years ago
parent
commit
a2c736de46
  1. 12
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  2. 14
      src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
  3. 124
      src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
  4. 5
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  5. 7
      src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
  6. 2
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  7. 43
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

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

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

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

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.OpenGL.Imaging; using Avalonia.OpenGL.Imaging;
using SkiaSharp; using SkiaSharp;
@ -15,6 +16,19 @@ namespace Avalonia.Skia
/// <param name="surfaces">Surfaces.</param> /// <param name="surfaces">Surfaces.</param>
/// <returns>Created render target or <see langword="null"/> if it fails.</returns> /// <returns>Created render target or <see langword="null"/> if it fails.</returns>
ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces); 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 WriteSurface { get; }
SKSurface ReadSurface { get; }
} }
public interface IOpenGlAwareSkiaGpu : ISkiaGpu public interface IOpenGlAwareSkiaGpu : ISkiaGpu

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

@ -0,0 +1,124 @@
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");
}
ReadSurface = SKSurface.Create(_grContext, new GRBackendTexture(pixelSize.Width, pixelSize.Height, false,
new GRGlTextureInfo(GL_TEXTURE_2D, (uint)_texture, (uint)InternalFormat)), SKColorType.Rgba8888);
var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 0,
new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat()));
WriteSurface = SKSurface.Create(_grContext, target,
GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
}
public void Dispose()
{
using (_glContext.EnsureCurrent())
{
WriteSurface.Dispose();
WriteSurface = null;
ReadSurface.Dispose();
ReadSurface = 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 WriteSurface { get; private set; }
public SKSurface ReadSurface { get; private set; }
}
}

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

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

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

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

43
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -13,10 +13,29 @@ namespace Avalonia.Skia
/// </summary> /// </summary>
internal class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl internal class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
{ {
private readonly SKSurface _surface; private readonly ISkiaSurface _surface;
private readonly SKCanvas _canvas; private readonly SKCanvas _canvas;
private readonly bool _disableLcdRendering; private readonly bool _disableLcdRendering;
private readonly GRContext _grContext; private readonly GRContext _grContext;
private readonly ISkiaGpu _gpu;
class SkiaSurfaceWrapper : ISkiaSurface
{
public SKSurface WriteSurface { get; private set; }
public SKSurface ReadSurface { get; private set; }
public SkiaSurfaceWrapper(SKSurface surface)
{
WriteSurface = ReadSurface = surface;
}
public void Dispose()
{
WriteSurface?.Dispose();
WriteSurface = null;
ReadSurface = null;
}
}
/// <summary> /// <summary>
/// Create new surface render target. /// Create new surface render target.
@ -29,9 +48,14 @@ namespace Avalonia.Skia
_disableLcdRendering = createInfo.DisableTextLcdRendering; _disableLcdRendering = createInfo.DisableTextLcdRendering;
_grContext = createInfo.GrContext; _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?.WriteSurface.Canvas;
if (_surface == null || _canvas == null) if (_surface == null || _canvas == null)
{ {
@ -70,11 +94,12 @@ namespace Avalonia.Skia
var createInfo = new DrawingContextImpl.CreateInfo var createInfo = new DrawingContextImpl.CreateInfo
{ {
Surface = _surface, Surface = _surface.WriteSurface,
Dpi = Dpi, Dpi = Dpi,
VisualBrushRenderer = visualBrushRenderer, VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = _disableLcdRendering, DisableTextLcdRendering = _disableLcdRendering,
GrContext = _grContext GrContext = _grContext,
Gpu = _gpu
}; };
return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++)); return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++));
@ -111,8 +136,8 @@ namespace Avalonia.Skia
{ {
if (sourceRect.Left == 0 && sourceRect.Top == 0 && sourceRect.Size == destRect.Size) if (sourceRect.Left == 0 && sourceRect.Top == 0 && sourceRect.Size == destRect.Size)
{ {
_surface.Canvas.Flush(); _surface.ReadSurface.Canvas.Flush();
_surface.Draw(context.Canvas, destRect.Left, destRect.Top, paint); _surface.ReadSurface.Draw(context.Canvas, destRect.Left, destRect.Top, paint);
} }
else else
using (var image = SnapshotImage()) using (var image = SnapshotImage())
@ -127,7 +152,7 @@ namespace Avalonia.Skia
/// <returns>Image snapshot.</returns> /// <returns>Image snapshot.</returns>
public SKImage SnapshotImage() public SKImage SnapshotImage()
{ {
return _surface.Snapshot(); return _surface.ReadSurface.Snapshot();
} }
/// <summary> /// <summary>
@ -178,6 +203,8 @@ namespace Avalonia.Skia
/// GPU-accelerated context (optional) /// GPU-accelerated context (optional)
/// </summary> /// </summary>
public GRContext GrContext; public GRContext GrContext;
public ISkiaGpu Gpu;
} }
} }
} }

Loading…
Cancel
Save