33 changed files with 5278 additions and 162 deletions
@ -0,0 +1,50 @@ |
|||
using System; |
|||
using System.Threading; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
public class DisposableLock |
|||
{ |
|||
private readonly object _lock = new object(); |
|||
|
|||
/// <summary>
|
|||
/// Tries to take a lock
|
|||
/// </summary>
|
|||
/// <returns>IDisposable if succeeded to obtain the lock</returns>
|
|||
public IDisposable TryLock() |
|||
{ |
|||
if (Monitor.TryEnter(_lock)) |
|||
return new UnlockDisposable(_lock); |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Enters a waiting lock
|
|||
/// </summary>
|
|||
public IDisposable Lock() |
|||
{ |
|||
Monitor.Enter(_lock); |
|||
return new UnlockDisposable(_lock); |
|||
} |
|||
|
|||
private sealed class UnlockDisposable : IDisposable |
|||
{ |
|||
private object _lock; |
|||
|
|||
public UnlockDisposable(object @lock) |
|||
{ |
|||
_lock = @lock; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
object @lock = Interlocked.Exchange(ref _lock, null); |
|||
|
|||
if (@lock != null) |
|||
{ |
|||
Monitor.Exit(@lock); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -1,8 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public interface IGlContext |
|||
public interface IGlContext : IDisposable |
|||
{ |
|||
IGlDisplay Display { get; } |
|||
void MakeCurrent(); |
|||
IDisposable MakeCurrent(); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,9 @@ |
|||
using Avalonia.OpenGL.Imaging; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public interface IOpenGlAwarePlatformRenderInterface |
|||
{ |
|||
IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap(); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using Avalonia.Media.Imaging; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.OpenGL.Imaging |
|||
{ |
|||
public interface IOpenGlTextureBitmapImpl : IBitmapImpl |
|||
{ |
|||
IDisposable Lock(); |
|||
void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling); |
|||
void SetDirty(); |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using Avalonia.Media; |
|||
using Avalonia.Media.Imaging; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Threading; |
|||
|
|||
namespace Avalonia.OpenGL.Imaging |
|||
{ |
|||
public class OpenGlTextureBitmap : Bitmap, IAffectsRender |
|||
{ |
|||
private IOpenGlTextureBitmapImpl _impl; |
|||
static IOpenGlTextureBitmapImpl CreateOrThrow() |
|||
{ |
|||
if (!(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is IOpenGlAwarePlatformRenderInterface |
|||
glAware)) |
|||
throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration"); |
|||
return glAware.CreateOpenGlTextureBitmap(); |
|||
} |
|||
|
|||
public OpenGlTextureBitmap() |
|||
: base(CreateOrThrow()) |
|||
{ |
|||
_impl = (IOpenGlTextureBitmapImpl)PlatformImpl.Item; |
|||
} |
|||
|
|||
public IDisposable Lock() => _impl.Lock(); |
|||
|
|||
public void SetTexture(int textureId, int internalFormat, PixelSize size, double dpiScaling) |
|||
{ |
|||
_impl.SetBackBuffer(textureId, internalFormat, size, dpiScaling); |
|||
SetIsDirty(); |
|||
} |
|||
|
|||
public void SetIsDirty() |
|||
{ |
|||
if (Dispatcher.UIThread.CheckAccess()) |
|||
CallInvalidated(); |
|||
else |
|||
Dispatcher.UIThread.Post(CallInvalidated); |
|||
} |
|||
|
|||
private void CallInvalidated() => Invalidated?.Invoke(this, EventArgs.Empty); |
|||
|
|||
public event EventHandler Invalidated; |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System.Collections.Generic; |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.OpenGL.Imaging; |
|||
using SkiaSharp; |
|||
|
|||
namespace Avalonia.Skia |
|||
{ |
|||
class GlSkiaGpu : IOpenGlAwareSkiaGpu |
|||
{ |
|||
private GRContext _grContext; |
|||
|
|||
public GlSkiaGpu(IWindowingPlatformGlFeature gl) |
|||
{ |
|||
|
|||
var immediateContext = gl.CreateContext(); |
|||
using (immediateContext.MakeCurrent()) |
|||
{ |
|||
var display = gl.Display; |
|||
using (var iface = display.Type == GlDisplayType.OpenGL2 ? |
|||
GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc)) : |
|||
GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc))) |
|||
{ |
|||
_grContext = GRContext.Create(GRBackend.OpenGL, iface); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces) |
|||
{ |
|||
foreach (var surface in surfaces) |
|||
{ |
|||
if (surface is IGlPlatformSurface glSurface) |
|||
{ |
|||
return new GlRenderTarget(_grContext, glSurface); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap() => new OpenGlTextureBitmapImpl(); |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
using System; |
|||
using System.IO; |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.OpenGL.Imaging; |
|||
using Avalonia.Skia.Helpers; |
|||
using Avalonia.Utilities; |
|||
using SkiaSharp; |
|||
|
|||
namespace Avalonia.Skia |
|||
{ |
|||
class OpenGlTextureBitmapImpl : IOpenGlTextureBitmapImpl, IDrawableBitmapImpl |
|||
{ |
|||
private DisposableLock _lock = new DisposableLock(); |
|||
private int _textureId; |
|||
private int _internalFormat; |
|||
|
|||
public void Dispose() |
|||
{ |
|||
using (Lock()) |
|||
{ |
|||
_textureId = 0; |
|||
PixelSize = new PixelSize(1, 1); |
|||
Version++; |
|||
} |
|||
} |
|||
|
|||
public Vector Dpi { get; private set; } = new Vector(96, 96); |
|||
public PixelSize PixelSize { get; private set; } = new PixelSize(1, 1); |
|||
public int Version { get; private set; } = 0; |
|||
|
|||
public void Save(string fileName) => throw new System.NotSupportedException(); |
|||
public void Save(Stream stream) => throw new System.NotSupportedException(); |
|||
|
|||
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint) |
|||
{ |
|||
// For now silently ignore
|
|||
if (context.GrContext == null) |
|||
return; |
|||
|
|||
using (Lock()) |
|||
{ |
|||
if (_textureId == 0) |
|||
return; |
|||
using (var backendTexture = new GRBackendTexture(PixelSize.Width, PixelSize.Height, false, |
|||
new GRGlTextureInfo( |
|||
GlConsts.GL_TEXTURE_2D, (uint)_textureId, |
|||
(uint)_internalFormat))) |
|||
using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.TopLeft, |
|||
SKColorType.Rgba8888)) |
|||
{ |
|||
// Again, silently ignore, if something went wrong it's not our fault
|
|||
if (surface == null) |
|||
return; |
|||
|
|||
using (var snapshot = surface.Snapshot()) |
|||
context.Canvas.DrawImage(snapshot, sourceRect, destRect, paint); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public IDisposable Lock() => _lock.Lock(); |
|||
|
|||
public void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling) |
|||
{ |
|||
using (_lock.Lock()) |
|||
{ |
|||
_textureId = textureId; |
|||
_internalFormat = internalFormat; |
|||
PixelSize = pixelSize; |
|||
Dpi = new Vector(96 * dpiScaling, 96 * dpiScaling); |
|||
Version++; |
|||
} |
|||
} |
|||
|
|||
public void SetDirty() |
|||
{ |
|||
using (_lock.Lock()) |
|||
Version++; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue