52 changed files with 786 additions and 486 deletions
@ -1,6 +1,6 @@ |
|||
// ReSharper disable UnusedMember.Global
|
|||
// ReSharper disable IdentifierTypo
|
|||
namespace Avalonia.OpenGL |
|||
namespace Avalonia.OpenGL.Egl |
|||
{ |
|||
public static class EglConsts |
|||
{ |
|||
@ -1,4 +1,4 @@ |
|||
namespace Avalonia.OpenGL |
|||
namespace Avalonia.OpenGL.Egl |
|||
{ |
|||
public enum EglErrors |
|||
{ |
|||
@ -0,0 +1,54 @@ |
|||
using Avalonia.OpenGL.Surfaces; |
|||
|
|||
namespace Avalonia.OpenGL.Egl |
|||
{ |
|||
public class EglGlPlatformSurface : EglGlPlatformSurfaceBase |
|||
{ |
|||
private readonly EglPlatformOpenGlInterface _egl; |
|||
private readonly IEglWindowGlPlatformSurfaceInfo _info; |
|||
|
|||
public EglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base() |
|||
{ |
|||
_egl = egl; |
|||
_info = info; |
|||
} |
|||
|
|||
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() |
|||
{ |
|||
var glSurface = _egl.CreateWindowSurface(_info.Handle); |
|||
return new RenderTarget(_egl, glSurface, _info); |
|||
} |
|||
|
|||
class RenderTarget : EglPlatformSurfaceRenderTargetBase |
|||
{ |
|||
private readonly EglPlatformOpenGlInterface _egl; |
|||
private EglSurface _glSurface; |
|||
private readonly IEglWindowGlPlatformSurfaceInfo _info; |
|||
private PixelSize _currentSize; |
|||
|
|||
public RenderTarget(EglPlatformOpenGlInterface egl, |
|||
EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(egl) |
|||
{ |
|||
_egl = egl; |
|||
_glSurface = glSurface; |
|||
_info = info; |
|||
_currentSize = info.Size; |
|||
} |
|||
|
|||
public override void Dispose() => _glSurface.Dispose(); |
|||
|
|||
public override IGlPlatformSurfaceRenderingSession BeginDraw() |
|||
{ |
|||
if (_info.Size != _currentSize || _glSurface == null) |
|||
{ |
|||
_glSurface?.Dispose(); |
|||
_glSurface = null; |
|||
_glSurface = _egl.CreateWindowSurface(_info.Handle); |
|||
_currentSize = _info.Size; |
|||
} |
|||
return base.BeginDraw(_glSurface, _info); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,72 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using static Avalonia.OpenGL.Egl.EglConsts; |
|||
|
|||
namespace Avalonia.OpenGL.Egl |
|||
{ |
|||
public class EglPlatformOpenGlInterface : IPlatformOpenGlInterface |
|||
{ |
|||
public EglDisplay Display { get; private set; } |
|||
public bool CanCreateContexts => true; |
|||
public bool CanShareContexts => Display.SupportsSharing; |
|||
|
|||
public EglContext PrimaryEglContext { get; } |
|||
public IGlContext PrimaryContext => PrimaryEglContext; |
|||
|
|||
public EglPlatformOpenGlInterface(EglDisplay display) |
|||
{ |
|||
Display = display; |
|||
PrimaryEglContext = display.CreateContext(null); |
|||
} |
|||
|
|||
public static void TryInitialize() |
|||
{ |
|||
var feature = TryCreate(); |
|||
if (feature != null) |
|||
AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(feature); |
|||
} |
|||
|
|||
public static EglPlatformOpenGlInterface TryCreate() => TryCreate(() => new EglDisplay()); |
|||
public static EglPlatformOpenGlInterface TryCreate(Func<EglDisplay> displayFactory) |
|||
{ |
|||
try |
|||
{ |
|||
return new EglPlatformOpenGlInterface(displayFactory()); |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public IGlContext CreateContext() => Display.CreateContext(null); |
|||
public IGlContext CreateSharedContext() => Display.CreateContext(PrimaryEglContext); |
|||
|
|||
|
|||
public EglSurface CreateWindowSurface(IntPtr window) |
|||
{ |
|||
using (PrimaryContext.MakeCurrent()) |
|||
{ |
|||
var s = Display.EglInterface.CreateWindowSurface(Display.Handle, Display.Config, window, |
|||
new[] { EGL_NONE, EGL_NONE }); |
|||
if (s == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreateWindowSurface", Display.EglInterface); |
|||
return new EglSurface(Display, PrimaryEglContext, s); |
|||
} |
|||
} |
|||
|
|||
public EglSurface CreatePBufferFromClientBuffer (int bufferType, IntPtr handle, int[] attribs) |
|||
{ |
|||
using (PrimaryContext.MakeCurrent()) |
|||
{ |
|||
var s = Display.EglInterface.CreatePbufferFromClientBuffer(Display.Handle, bufferType, handle, |
|||
Display.Config, attribs); |
|||
|
|||
if (s == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreatePbufferFromClientBuffer", Display.EglInterface); |
|||
return new EglSurface(Display, PrimaryEglContext, s); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,22 +1,25 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
namespace Avalonia.OpenGL.Egl |
|||
{ |
|||
public class EglSurface : SafeHandle |
|||
{ |
|||
private readonly EglDisplay _display; |
|||
private readonly EglContext _context; |
|||
private readonly EglInterface _egl; |
|||
|
|||
public EglSurface(EglDisplay display, EglInterface egl, IntPtr surface) : base(surface, true) |
|||
public EglSurface(EglDisplay display, EglContext context, IntPtr surface) : base(surface, true) |
|||
{ |
|||
_display = display; |
|||
_egl = egl; |
|||
_context = context; |
|||
_egl = display.EglInterface; |
|||
} |
|||
|
|||
protected override bool ReleaseHandle() |
|||
{ |
|||
_egl.DestroySurface(_display.Handle, handle); |
|||
using (_context.MakeCurrent()) |
|||
_egl.DestroySurface(_display.Handle, handle); |
|||
return true; |
|||
} |
|||
|
|||
@ -1,43 +0,0 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public class EglGlPlatformFeature : IWindowingPlatformGlFeature |
|||
{ |
|||
private EglDisplay _display; |
|||
public EglDisplay Display => _display; |
|||
public IGlContext CreateContext() |
|||
{ |
|||
return _display.CreateContext(DeferredContext); |
|||
} |
|||
public EglContext DeferredContext { get; private set; } |
|||
public IGlContext MainContext => DeferredContext; |
|||
|
|||
public static void TryInitialize() |
|||
{ |
|||
var feature = TryCreate(); |
|||
if (feature != null) |
|||
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature); |
|||
} |
|||
|
|||
public static EglGlPlatformFeature TryCreate() => TryCreate(() => new EglDisplay()); |
|||
public static EglGlPlatformFeature TryCreate(Func<EglDisplay> displayFactory) |
|||
{ |
|||
try |
|||
{ |
|||
var disp = displayFactory(); |
|||
return new EglGlPlatformFeature |
|||
{ |
|||
_display = disp, |
|||
DeferredContext = disp.CreateContext(null) |
|||
}; |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e); |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,51 +0,0 @@ |
|||
using System; |
|||
using System.Threading; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public class EglGlPlatformSurface : EglGlPlatformSurfaceBase |
|||
{ |
|||
private readonly EglDisplay _display; |
|||
private readonly EglContext _context; |
|||
private readonly IEglWindowGlPlatformSurfaceInfo _info; |
|||
|
|||
public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base() |
|||
{ |
|||
_display = context.Display; |
|||
_context = context; |
|||
_info = info; |
|||
} |
|||
|
|||
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() |
|||
{ |
|||
var glSurface = _display.CreateWindowSurface(_info.Handle); |
|||
return new RenderTarget(_display, _context, glSurface, _info); |
|||
} |
|||
|
|||
class RenderTarget : EglPlatformSurfaceRenderTargetBase |
|||
{ |
|||
private readonly EglDisplay _display; |
|||
private readonly EglContext _context; |
|||
private readonly EglSurface _glSurface; |
|||
private readonly IEglWindowGlPlatformSurfaceInfo _info; |
|||
private PixelSize _initialSize; |
|||
|
|||
public RenderTarget(EglDisplay display, EglContext context, |
|||
EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context) |
|||
{ |
|||
_display = display; |
|||
_context = context; |
|||
_glSurface = glSurface; |
|||
_info = info; |
|||
_initialSize = info.Size; |
|||
} |
|||
|
|||
public override void Dispose() => _glSurface.Dispose(); |
|||
|
|||
public override bool IsCorrupted => _initialSize != _info.Size; |
|||
|
|||
public override IGlPlatformSurfaceRenderingSession BeginDraw() => base.BeginDraw(_glSurface, _info); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,13 @@ |
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public interface IPlatformOpenGlInterface |
|||
{ |
|||
IGlContext PrimaryContext { get; } |
|||
IGlContext CreateSharedContext(); |
|||
bool CanShareContexts { get; } |
|||
bool CanCreateContexts { get; } |
|||
IGlContext CreateContext(); |
|||
/*IGlContext TryCreateContext(GlVersion version); |
|||
*/ |
|||
} |
|||
} |
|||
@ -1,8 +0,0 @@ |
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public interface IWindowingPlatformGlFeature |
|||
{ |
|||
IGlContext CreateContext(); |
|||
IGlContext MainContext { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Avalonia.Media.Imaging; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.OpenGL.Imaging |
|||
{ |
|||
public interface IOpenGlBitmapImpl : IBitmapImpl |
|||
{ |
|||
IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback); |
|||
bool SupportsContext(IGlContext context); |
|||
} |
|||
|
|||
public interface IOpenGlBitmapAttachment : IDisposable |
|||
{ |
|||
void Present(); |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
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(); |
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
namespace Avalonia.OpenGL |
|||
namespace Avalonia.OpenGL.Surfaces |
|||
{ |
|||
public interface IGlPlatformSurface |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
namespace Avalonia.OpenGL.Surfaces |
|||
{ |
|||
public interface IGlPlatformSurfaceRenderTarget : IDisposable |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
namespace Avalonia.OpenGL.Surfaces |
|||
{ |
|||
public interface IGlPlatformSurfaceRenderingSession : IDisposable |
|||
{ |
|||
@ -0,0 +1,9 @@ |
|||
using Avalonia.OpenGL; |
|||
|
|||
namespace Avalonia.LinuxFramebuffer.Output |
|||
{ |
|||
public interface IGlOutputBackend : IOutputBackend |
|||
{ |
|||
public IPlatformOpenGlInterface PlatformOpenGlInterface { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.OpenGL.Imaging; |
|||
using Avalonia.Utilities; |
|||
using SkiaSharp; |
|||
using static Avalonia.OpenGL.GlConsts; |
|||
|
|||
namespace Avalonia.Skia |
|||
{ |
|||
class GlOpenGlBitmapImpl : IOpenGlBitmapImpl, IDrawableBitmapImpl |
|||
{ |
|||
private readonly IGlContext _context; |
|||
private readonly object _lock = new object(); |
|||
private IGlPresentableOpenGlSurface _surface; |
|||
|
|||
public GlOpenGlBitmapImpl(IGlContext context, PixelSize pixelSize, Vector dpi) |
|||
{ |
|||
_context = context; |
|||
PixelSize = pixelSize; |
|||
Dpi = dpi; |
|||
} |
|||
|
|||
public Vector Dpi { get; } |
|||
public PixelSize PixelSize { get; } |
|||
public int Version { get; private set; } |
|||
public void Save(string fileName) => throw new NotSupportedException(); |
|||
|
|||
public void Save(Stream stream) => throw new NotSupportedException(); |
|||
|
|||
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint) |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (_surface == null) |
|||
return; |
|||
using (_surface.Lock()) |
|||
{ |
|||
using (var backendTexture = new GRBackendTexture(PixelSize.Width, PixelSize.Height, false, |
|||
new GRGlTextureInfo( |
|||
GlConsts.GL_TEXTURE_2D, (uint)_surface.GetTextureId(), |
|||
(uint)_surface.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 IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback) |
|||
{ |
|||
if (!SupportsContext(context)) |
|||
throw new OpenGlException("Context is not supported for texture sharing"); |
|||
return new SharedOpenGlBitmapAttachment(this, context, presentCallback); |
|||
} |
|||
|
|||
public bool SupportsContext(IGlContext context) |
|||
{ |
|||
// TODO: negotiated platform surface sharing
|
|||
return _context.IsSharedWith(context); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
|
|||
} |
|||
|
|||
internal void Present(IGlPresentableOpenGlSurface surface) |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
_surface = surface; |
|||
} |
|||
} |
|||
} |
|||
|
|||
interface IGlPresentableOpenGlSurface : IDisposable |
|||
{ |
|||
int GetTextureId(); |
|||
int InternalFormat { get; } |
|||
IDisposable Lock(); |
|||
} |
|||
|
|||
class SharedOpenGlBitmapAttachment : IOpenGlBitmapAttachment, IGlPresentableOpenGlSurface |
|||
{ |
|||
private readonly GlOpenGlBitmapImpl _bitmap; |
|||
private readonly IGlContext _context; |
|||
private readonly Action _presentCallback; |
|||
private readonly int _fbo; |
|||
private readonly int _texture; |
|||
private readonly int _frontBuffer; |
|||
private bool _disposed; |
|||
private readonly DisposableLock _lock = new DisposableLock(); |
|||
|
|||
public SharedOpenGlBitmapAttachment(GlOpenGlBitmapImpl bitmap, IGlContext context, Action presentCallback) |
|||
{ |
|||
_bitmap = bitmap; |
|||
_context = context; |
|||
_presentCallback = presentCallback; |
|||
using (_context.EnsureCurrent()) |
|||
{ |
|||
var glVersion = _context.Version; |
|||
InternalFormat = glVersion.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8; |
|||
|
|||
_context.GlInterface.GetIntegerv(GL_FRAMEBUFFER_BINDING, out _fbo); |
|||
if (_fbo == 0) |
|||
throw new OpenGlException("Current FBO is 0"); |
|||
|
|||
{ |
|||
var gl = _context.GlInterface; |
|||
|
|||
var textures = new int[2]; |
|||
gl.GenTextures(2, textures); |
|||
_texture = textures[0]; |
|||
_frontBuffer = textures[1]; |
|||
|
|||
gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture); |
|||
foreach (var t in textures) |
|||
{ |
|||
gl.BindTexture(GL_TEXTURE_2D, t); |
|||
gl.TexImage2D(GL_TEXTURE_2D, 0, |
|||
InternalFormat, |
|||
_bitmap.PixelSize.Width, _bitmap.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); |
|||
gl.BindTexture(GL_TEXTURE_2D, oldTexture); |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Present() |
|||
{ |
|||
using (_context.MakeCurrent()) |
|||
{ |
|||
if (_disposed) |
|||
throw new ObjectDisposedException(nameof(SharedOpenGlBitmapAttachment)); |
|||
|
|||
var gl = _context.GlInterface; |
|||
|
|||
gl.Finish(); |
|||
using (Lock()) |
|||
{ |
|||
gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFbo); |
|||
gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture); |
|||
gl.GetIntegerv(GL_ACTIVE_TEXTURE, out var oldActive); |
|||
|
|||
gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo); |
|||
gl.BindTexture(GL_TEXTURE_2D, _frontBuffer); |
|||
gl.ActiveTexture(GL_TEXTURE0); |
|||
|
|||
gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _bitmap.PixelSize.Width, |
|||
_bitmap.PixelSize.Height); |
|||
|
|||
gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo); |
|||
gl.BindTexture(GL_TEXTURE_2D, oldTexture); |
|||
gl.ActiveTexture(oldActive); |
|||
|
|||
gl.Finish(); |
|||
} |
|||
} |
|||
|
|||
_bitmap.Present(this); |
|||
_presentCallback(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
var gl = _context.GlInterface; |
|||
_bitmap.Present(null); |
|||
|
|||
if(_disposed) |
|||
return; |
|||
using (_context.MakeCurrent()) |
|||
using (Lock()) |
|||
{ |
|||
if(_disposed) |
|||
return; |
|||
_disposed = true; |
|||
gl.DeleteTextures(2, new[] { _texture, _frontBuffer }); |
|||
} |
|||
} |
|||
|
|||
int IGlPresentableOpenGlSurface.GetTextureId() |
|||
{ |
|||
return _frontBuffer; |
|||
} |
|||
|
|||
public int InternalFormat { get; } |
|||
|
|||
public IDisposable Lock() => _lock.Lock(); |
|||
} |
|||
} |
|||
@ -1,81 +0,0 @@ |
|||
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