@ -0,0 +1,61 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Metadata; |
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines a composite page transition that can be used to combine multiple transitions.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// <para>
|
||||
|
/// Instantiate the <see cref="CompositePageTransition" /> in XAML and initialize the
|
||||
|
/// <see cref="Transitions" /> property in order to have many animations triggered at once.
|
||||
|
/// For example, you can combine <see cref="CrossFade"/> and <see cref="PageSlide"/>.
|
||||
|
/// <code>
|
||||
|
/// <![CDATA[
|
||||
|
/// <reactiveUi:RoutedViewHost Router="{Binding Router}">
|
||||
|
/// <reactiveUi:RoutedViewHost.PageTransition>
|
||||
|
/// <CompositePageTransition>
|
||||
|
/// <PageSlide Duration="0.5" />
|
||||
|
/// <CrossFade Duration="0.5" />
|
||||
|
/// </CompositePageTransition>
|
||||
|
/// </reactiveUi:RoutedViewHost.PageTransition>
|
||||
|
/// </reactiveUi:RoutedViewHost>
|
||||
|
/// ]]>
|
||||
|
/// </code>
|
||||
|
/// </para>
|
||||
|
/// </remarks>
|
||||
|
public class CompositePageTransition : IPageTransition |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the transitions to be executed. Can be defined from XAML.
|
||||
|
/// </summary>
|
||||
|
[Content] |
||||
|
public List<IPageTransition> PageTransitions { get; set; } = new List<IPageTransition>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Starts the animation.
|
||||
|
/// </summary>
|
||||
|
/// <param name="from">
|
||||
|
/// The control that is being transitioned away from. May be null.
|
||||
|
/// </param>
|
||||
|
/// <param name="to">
|
||||
|
/// The control that is being transitioned to. May be null.
|
||||
|
/// </param>
|
||||
|
/// <param name="forward">
|
||||
|
/// Defines the direction of the transition.
|
||||
|
/// </param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that tracks the progress of the animation.
|
||||
|
/// </returns>
|
||||
|
public Task Start(Visual from, Visual to, bool forward) |
||||
|
{ |
||||
|
var transitionTasks = PageTransitions |
||||
|
.Select(transition => transition.Start(from, to, forward)) |
||||
|
.ToList(); |
||||
|
return Task.WhenAll(transitionTasks); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,134 @@ |
|||||
|
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, 8, |
||||
|
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(SKCanvas canvas) |
||||
|
{ |
||||
|
// This should set the render target as the current FBO
|
||||
|
// which is definitely not the best method, but it works
|
||||
|
canvas.Clear(); |
||||
|
canvas.Flush(); |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia.Helpers |
||||
|
{ |
||||
|
public class DrawingContextHelper |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Wrap Skia canvas in drawing context so we can use Avalonia api to render to external skia canvas
|
||||
|
/// this is useful in scenarios where canvas is not controlled by application, but received from another non avalonia api
|
||||
|
/// like: SKCanvas canvas = SKDocument.BeginPage(...);
|
||||
|
/// </summary>
|
||||
|
/// <param name="canvas"></param>
|
||||
|
/// <param name="dpi"></param>
|
||||
|
/// <param name="visualBrushRenderer"></param>
|
||||
|
/// <returns>DrawingContext</returns>
|
||||
|
public static IDrawingContextImpl WrapSkiaCanvas(SKCanvas canvas, Vector dpi, IVisualBrushRenderer visualBrushRenderer = null) |
||||
|
{ |
||||
|
var createInfo = new DrawingContextImpl.CreateInfo |
||||
|
{ |
||||
|
Canvas = canvas, |
||||
|
Dpi = dpi, |
||||
|
VisualBrushRenderer = visualBrushRenderer, |
||||
|
DisableTextLcdRendering = true, |
||||
|
}; |
||||
|
|
||||
|
return new DrawingContextImpl(createInfo); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
namespace Avalonia.Win32.OpenGl |
||||
|
{ |
||||
|
internal class WglConsts |
||||
|
{ |
||||
|
public const int WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091; |
||||
|
public const int WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092; |
||||
|
public const int WGL_CONTEXT_LAYER_PLANE_ARB = 0x2093; |
||||
|
public const int WGL_CONTEXT_FLAGS_ARB = 0x2094; |
||||
|
public const int WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126; |
||||
|
public const int WGL_NUMBER_PIXEL_FORMATS_ARB = 0x2000; |
||||
|
|
||||
|
public const int WGL_DRAW_TO_WINDOW_ARB = 0x2001; |
||||
|
public const int WGL_DRAW_TO_BITMAP_ARB = 0x2002; |
||||
|
public const int WGL_ACCELERATION_ARB = 0x2003; |
||||
|
public const int WGL_NEED_PALETTE_ARB = 0x2004; |
||||
|
public const int WGL_NEED_SYSTEM_PALETTE_ARB = 0x2005; |
||||
|
public const int WGL_SWAP_LAYER_BUFFERS_ARB = 0x2006; |
||||
|
public const int WGL_SWAP_METHOD_ARB = 0x2007; |
||||
|
public const int WGL_NUMBER_OVERLAYS_ARB = 0x2008; |
||||
|
public const int WGL_NUMBER_UNDERLAYS_ARB = 0x2009; |
||||
|
public const int WGL_TRANSPARENT_ARB = 0x200A; |
||||
|
public const int WGL_TRANSPARENT_RED_VALUE_ARB = 0x2037; |
||||
|
public const int WGL_TRANSPARENT_GREEN_VALUE_ARB = 0x2038; |
||||
|
public const int WGL_TRANSPARENT_BLUE_VALUE_ARB = 0x2039; |
||||
|
public const int WGL_TRANSPARENT_ALPHA_VALUE_ARB = 0x203A; |
||||
|
public const int WGL_TRANSPARENT_INDEX_VALUE_ARB = 0x203B; |
||||
|
public const int WGL_SHARE_DEPTH_ARB = 0x200C; |
||||
|
public const int WGL_SHARE_STENCIL_ARB = 0x200D; |
||||
|
public const int WGL_SHARE_ACCUM_ARB = 0x200E; |
||||
|
public const int WGL_SUPPORT_GDI_ARB = 0x200F; |
||||
|
public const int WGL_SUPPORT_OPENGL_ARB = 0x2010; |
||||
|
public const int WGL_DOUBLE_BUFFER_ARB = 0x2011; |
||||
|
public const int WGL_STEREO_ARB = 0x2012; |
||||
|
public const int WGL_PIXEL_TYPE_ARB = 0x2013; |
||||
|
public const int WGL_COLOR_BITS_ARB = 0x2014; |
||||
|
public const int WGL_RED_BITS_ARB = 0x2015; |
||||
|
public const int WGL_RED_SHIFT_ARB = 0x2016; |
||||
|
public const int WGL_GREEN_BITS_ARB = 0x2017; |
||||
|
public const int WGL_GREEN_SHIFT_ARB = 0x2018; |
||||
|
public const int WGL_BLUE_BITS_ARB = 0x2019; |
||||
|
public const int WGL_BLUE_SHIFT_ARB = 0x201A; |
||||
|
public const int WGL_ALPHA_BITS_ARB = 0x201B; |
||||
|
public const int WGL_ALPHA_SHIFT_ARB = 0x201C; |
||||
|
public const int WGL_ACCUM_BITS_ARB = 0x201D; |
||||
|
public const int WGL_ACCUM_RED_BITS_ARB = 0x201E; |
||||
|
public const int WGL_ACCUM_GREEN_BITS_ARB = 0x201F; |
||||
|
public const int WGL_ACCUM_BLUE_BITS_ARB = 0x2020; |
||||
|
public const int WGL_ACCUM_ALPHA_BITS_ARB = 0x2021; |
||||
|
public const int WGL_DEPTH_BITS_ARB = 0x2022; |
||||
|
public const int WGL_STENCIL_BITS_ARB = 0x2023; |
||||
|
public const int WGL_AUX_BUFFERS_ARB = 0x2024; |
||||
|
public const int WGL_NO_ACCELERATION_ARB = 0x2025; |
||||
|
public const int WGL_GENERIC_ACCELERATION_ARB = 0x2026; |
||||
|
public const int WGL_FULL_ACCELERATION_ARB = 0x2027; |
||||
|
public const int WGL_SWAP_EXCHANGE_ARB = 0x2028; |
||||
|
public const int WGL_SWAP_COPY_ARB = 0x2029; |
||||
|
public const int WGL_SWAP_UNDEFINED_ARB = 0x202A; |
||||
|
public const int WGL_TYPE_RGBA_ARB = 0x202B; |
||||
|
public const int WGL_TYPE_COLORINDEX_ARB = 0x202C; |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
using System; |
||||
|
using System.Reactive.Disposables; |
||||
|
using Avalonia.OpenGL; |
||||
|
using Avalonia.Win32.Interop; |
||||
|
using static Avalonia.Win32.Interop.UnmanagedMethods; |
||||
|
using static Avalonia.Win32.OpenGl.WglConsts; |
||||
|
|
||||
|
namespace Avalonia.Win32.OpenGl |
||||
|
{ |
||||
|
class WglContext : IGlContext |
||||
|
{ |
||||
|
private object _lock = new object(); |
||||
|
private readonly WglContext _sharedWith; |
||||
|
private readonly IntPtr _context; |
||||
|
private readonly IntPtr _hWnd; |
||||
|
private readonly IntPtr _dc; |
||||
|
private readonly int _pixelFormat; |
||||
|
private readonly PixelFormatDescriptor _formatDescriptor; |
||||
|
public IntPtr Handle => _context; |
||||
|
|
||||
|
public WglContext(WglContext sharedWith, GlVersion version, IntPtr context, IntPtr hWnd, IntPtr dc, int pixelFormat, |
||||
|
PixelFormatDescriptor formatDescriptor) |
||||
|
{ |
||||
|
Version = version; |
||||
|
_sharedWith = sharedWith; |
||||
|
_context = context; |
||||
|
_hWnd = hWnd; |
||||
|
_dc = dc; |
||||
|
_pixelFormat = pixelFormat; |
||||
|
_formatDescriptor = formatDescriptor; |
||||
|
StencilSize = formatDescriptor.StencilBits; |
||||
|
using (MakeCurrent()) |
||||
|
GlInterface = new GlInterface(version, proc => |
||||
|
{ |
||||
|
var ext = wglGetProcAddress(proc); |
||||
|
if (ext != IntPtr.Zero) |
||||
|
return ext; |
||||
|
return GetProcAddress(WglDisplay.OpenGl32Handle, proc); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
wglDeleteContext(_context); |
||||
|
ReleaseDC(_hWnd, _dc); |
||||
|
DestroyWindow(_hWnd); |
||||
|
} |
||||
|
|
||||
|
public GlVersion Version { get; } |
||||
|
public GlInterface GlInterface { get; } |
||||
|
public int SampleCount { get; } |
||||
|
public int StencilSize { get; } |
||||
|
|
||||
|
private bool IsCurrent => wglGetCurrentContext() == _context && wglGetCurrentDC() == _dc; |
||||
|
public IDisposable MakeCurrent() |
||||
|
{ |
||||
|
if(IsCurrent) |
||||
|
return Disposable.Empty; |
||||
|
return new WglRestoreContext(_dc, _context, _lock); |
||||
|
} |
||||
|
|
||||
|
public IDisposable EnsureCurrent() => MakeCurrent(); |
||||
|
|
||||
|
|
||||
|
public IntPtr CreateConfiguredDeviceContext(IntPtr hWnd) |
||||
|
{ |
||||
|
var dc = GetDC(hWnd); |
||||
|
var fmt = _formatDescriptor; |
||||
|
SetPixelFormat(dc, _pixelFormat, ref fmt); |
||||
|
return dc; |
||||
|
} |
||||
|
|
||||
|
public IDisposable MakeCurrent(IntPtr hdc) => new WglRestoreContext(hdc, _context, _lock); |
||||
|
|
||||
|
public bool IsSharedWith(IGlContext context) |
||||
|
{ |
||||
|
var c = (WglContext)context; |
||||
|
return c == this |
||||
|
|| c._sharedWith == this |
||||
|
|| _sharedWith == context |
||||
|
|| _sharedWith != null && _sharedWith == c._sharedWith; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,183 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.OpenGL; |
||||
|
using Avalonia.Win32.Interop; |
||||
|
using static Avalonia.Win32.Interop.UnmanagedMethods; |
||||
|
using static Avalonia.Win32.OpenGl.WglConsts; |
||||
|
namespace Avalonia.Win32.OpenGl |
||||
|
{ |
||||
|
internal class WglDisplay |
||||
|
{ |
||||
|
private static bool? _initialized; |
||||
|
private static ushort _windowClass; |
||||
|
private static readonly WndProc _wndProcDelegate = WndProc; |
||||
|
private static readonly DebugCallbackDelegate _debugCallback = DebugCallback; |
||||
|
|
||||
|
private static IntPtr _bootstrapContext; |
||||
|
private static IntPtr _bootstrapWindow; |
||||
|
private static IntPtr _bootstrapDc; |
||||
|
private static PixelFormatDescriptor _defaultPfd; |
||||
|
private static int _defaultPixelFormat; |
||||
|
public static IntPtr OpenGl32Handle = LoadLibrary("opengl32"); |
||||
|
|
||||
|
private delegate bool WglChoosePixelFormatARBDelegate(IntPtr hdc, int[] piAttribIList, float[] pfAttribFList, |
||||
|
int nMaxFormats, int[] piFormats, out int nNumFormats); |
||||
|
|
||||
|
private static WglChoosePixelFormatARBDelegate WglChoosePixelFormatArb; |
||||
|
|
||||
|
private delegate IntPtr WglCreateContextAttribsARBDelegate(IntPtr hDC, IntPtr hShareContext, int[] attribList); |
||||
|
|
||||
|
private static WglCreateContextAttribsARBDelegate WglCreateContextAttribsArb; |
||||
|
|
||||
|
private delegate void GlDebugMessageCallbackDelegate(IntPtr callback, IntPtr userParam); |
||||
|
|
||||
|
private static GlDebugMessageCallbackDelegate GlDebugMessageCallback; |
||||
|
|
||||
|
private delegate void DebugCallbackDelegate(int source, int type, int id, int severity, int len, IntPtr message, |
||||
|
IntPtr userParam); |
||||
|
|
||||
|
static bool Initialize() |
||||
|
{ |
||||
|
if (!_initialized.HasValue) |
||||
|
_initialized = InitializeCore(); |
||||
|
return _initialized.Value; |
||||
|
} |
||||
|
static bool InitializeCore() |
||||
|
{ |
||||
|
var wndClassEx = new WNDCLASSEX |
||||
|
{ |
||||
|
cbSize = Marshal.SizeOf<WNDCLASSEX>(), |
||||
|
hInstance = GetModuleHandle(null), |
||||
|
lpfnWndProc = _wndProcDelegate, |
||||
|
lpszClassName = "AvaloniaGlWindow-" + Guid.NewGuid(), |
||||
|
style = (int)ClassStyles.CS_OWNDC |
||||
|
}; |
||||
|
|
||||
|
_windowClass = RegisterClassEx(ref wndClassEx); |
||||
|
_bootstrapWindow = CreateOffscreenWindow(); |
||||
|
_bootstrapDc = GetDC(_bootstrapWindow); |
||||
|
_defaultPfd = new PixelFormatDescriptor |
||||
|
{ |
||||
|
Size = (ushort)Marshal.SizeOf<PixelFormatDescriptor>(), |
||||
|
Version = 1, |
||||
|
Flags = PixelFormatDescriptorFlags.PFD_DRAW_TO_WINDOW | |
||||
|
PixelFormatDescriptorFlags.PFD_SUPPORT_OPENGL | PixelFormatDescriptorFlags.PFD_DOUBLEBUFFER, |
||||
|
DepthBits = 24, |
||||
|
StencilBits = 8, |
||||
|
ColorBits = 32 |
||||
|
}; |
||||
|
_defaultPixelFormat = ChoosePixelFormat(_bootstrapDc, ref _defaultPfd); |
||||
|
SetPixelFormat(_bootstrapDc, _defaultPixelFormat, ref _defaultPfd); |
||||
|
|
||||
|
_bootstrapContext = wglCreateContext(_bootstrapDc); |
||||
|
if (_bootstrapContext == IntPtr.Zero) |
||||
|
return false; |
||||
|
|
||||
|
wglMakeCurrent(_bootstrapDc, _bootstrapContext); |
||||
|
WglCreateContextAttribsArb = Marshal.GetDelegateForFunctionPointer<WglCreateContextAttribsARBDelegate>( |
||||
|
wglGetProcAddress("wglCreateContextAttribsARB")); |
||||
|
|
||||
|
WglChoosePixelFormatArb = |
||||
|
Marshal.GetDelegateForFunctionPointer<WglChoosePixelFormatARBDelegate>( |
||||
|
wglGetProcAddress("wglChoosePixelFormatARB")); |
||||
|
|
||||
|
GlDebugMessageCallback = |
||||
|
Marshal.GetDelegateForFunctionPointer<GlDebugMessageCallbackDelegate>( |
||||
|
wglGetProcAddress("glDebugMessageCallback")); |
||||
|
|
||||
|
|
||||
|
var formats = new int[1]; |
||||
|
WglChoosePixelFormatArb(_bootstrapDc, new int[] |
||||
|
{ |
||||
|
WGL_DRAW_TO_WINDOW_ARB, 1, |
||||
|
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, |
||||
|
WGL_SUPPORT_OPENGL_ARB, 1, |
||||
|
WGL_DOUBLE_BUFFER_ARB, 1, |
||||
|
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, |
||||
|
WGL_COLOR_BITS_ARB, 32, |
||||
|
WGL_DEPTH_BITS_ARB, 0, |
||||
|
WGL_STENCIL_BITS_ARB, 0, |
||||
|
0, // End
|
||||
|
}, null, 1, formats, out int numFormats); |
||||
|
if (numFormats != 0) |
||||
|
{ |
||||
|
DescribePixelFormat(_bootstrapDc, formats[0], Marshal.SizeOf<PixelFormatDescriptor>(), ref _defaultPfd); |
||||
|
_defaultPixelFormat = formats[0]; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
wglMakeCurrent(IntPtr.Zero, IntPtr.Zero); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) |
||||
|
{ |
||||
|
return DefWindowProc(hWnd, msg, wParam, lParam); |
||||
|
} |
||||
|
|
||||
|
private static void DebugCallback(int source, int type, int id, int severity, int len, IntPtr message, IntPtr userparam) |
||||
|
{ |
||||
|
var err = Marshal.PtrToStringAnsi(message, len); |
||||
|
Console.Error.WriteLine(err); |
||||
|
} |
||||
|
|
||||
|
public static WglContext CreateContext(GlVersion[] versions, IGlContext share) |
||||
|
{ |
||||
|
if (!Initialize()) |
||||
|
return null; |
||||
|
|
||||
|
var shareContext = (WglContext)share; |
||||
|
|
||||
|
using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null)) |
||||
|
{ |
||||
|
var window = CreateOffscreenWindow(); |
||||
|
var dc = GetDC(window); |
||||
|
SetPixelFormat(dc, _defaultPixelFormat, ref _defaultPfd); |
||||
|
foreach (var version in versions) |
||||
|
{ |
||||
|
if(version.Type != GlProfileType.OpenGL) |
||||
|
continue; |
||||
|
var context = WglCreateContextAttribsArb(dc, shareContext?.Handle ?? IntPtr.Zero, |
||||
|
new[] |
||||
|
{ |
||||
|
// major
|
||||
|
WGL_CONTEXT_MAJOR_VERSION_ARB, version.Major, |
||||
|
// minor
|
||||
|
WGL_CONTEXT_MINOR_VERSION_ARB, version.Minor, |
||||
|
// core profile
|
||||
|
WGL_CONTEXT_PROFILE_MASK_ARB, 1, |
||||
|
// debug
|
||||
|
// WGL_CONTEXT_FLAGS_ARB, 1,
|
||||
|
// end
|
||||
|
0, 0 |
||||
|
}); |
||||
|
using(new WglRestoreContext(dc, context, null)) |
||||
|
GlDebugMessageCallback(Marshal.GetFunctionPointerForDelegate(_debugCallback), IntPtr.Zero); |
||||
|
if (context != IntPtr.Zero) |
||||
|
return new WglContext(shareContext, version, context, window, dc, |
||||
|
_defaultPixelFormat, _defaultPfd); |
||||
|
} |
||||
|
|
||||
|
ReleaseDC(window, dc); |
||||
|
DestroyWindow(window); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static IntPtr CreateOffscreenWindow() => |
||||
|
CreateWindowEx( |
||||
|
0, |
||||
|
_windowClass, |
||||
|
null, |
||||
|
(int)WindowStyles.WS_OVERLAPPEDWINDOW, |
||||
|
0, |
||||
|
0, |
||||
|
640, |
||||
|
480, |
||||
|
IntPtr.Zero, |
||||
|
IntPtr.Zero, |
||||
|
IntPtr.Zero, |
||||
|
IntPtr.Zero); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
using System; |
||||
|
using System.Diagnostics; |
||||
|
using Avalonia.OpenGL; |
||||
|
using Avalonia.OpenGL.Egl; |
||||
|
using Avalonia.OpenGL.Surfaces; |
||||
|
using Avalonia.Win32.Interop; |
||||
|
using static Avalonia.OpenGL.GlConsts; |
||||
|
using static Avalonia.Win32.Interop.UnmanagedMethods; |
||||
|
namespace Avalonia.Win32.OpenGl |
||||
|
{ |
||||
|
class WglGlPlatformSurface: IGlPlatformSurface |
||||
|
{ |
||||
|
|
||||
|
private readonly WglContext _context; |
||||
|
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
||||
|
|
||||
|
public WglGlPlatformSurface(WglContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) |
||||
|
{ |
||||
|
_context = context; |
||||
|
_info = info; |
||||
|
} |
||||
|
|
||||
|
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() |
||||
|
{ |
||||
|
return new RenderTarget(_context, _info); |
||||
|
} |
||||
|
|
||||
|
class RenderTarget : IGlPlatformSurfaceRenderTarget |
||||
|
{ |
||||
|
private readonly WglContext _context; |
||||
|
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
||||
|
private IntPtr _hdc; |
||||
|
public RenderTarget(WglContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) |
||||
|
{ |
||||
|
_context = context; |
||||
|
_info = info; |
||||
|
_hdc = context.CreateConfiguredDeviceContext(info.Handle); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
UnmanagedMethods.ReleaseDC(_hdc, _info.Handle); |
||||
|
} |
||||
|
|
||||
|
public IGlPlatformSurfaceRenderingSession BeginDraw() |
||||
|
{ |
||||
|
var oldContext = _context.MakeCurrent(_hdc); |
||||
|
|
||||
|
// Reset to default FBO first
|
||||
|
_context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, 0); |
||||
|
|
||||
|
return new Session(_context, _hdc, _info, oldContext); |
||||
|
} |
||||
|
|
||||
|
class Session : IGlPlatformSurfaceRenderingSession |
||||
|
{ |
||||
|
private readonly WglContext _context; |
||||
|
private readonly IntPtr _hdc; |
||||
|
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; |
||||
|
private readonly IDisposable _clearContext; |
||||
|
public IGlContext Context => _context; |
||||
|
|
||||
|
public Session(WglContext context, IntPtr hdc, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info, |
||||
|
IDisposable clearContext) |
||||
|
{ |
||||
|
_context = context; |
||||
|
_hdc = hdc; |
||||
|
_info = info; |
||||
|
_clearContext = clearContext; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_context.GlInterface.Flush(); |
||||
|
UnmanagedMethods.SwapBuffers(_hdc); |
||||
|
_clearContext.Dispose(); |
||||
|
} |
||||
|
|
||||
|
public PixelSize Size => _info.Size; |
||||
|
public double Scaling => _info.Scaling; |
||||
|
public bool IsYFlipped { get; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.OpenGL; |
||||
|
|
||||
|
namespace Avalonia.Win32.OpenGl |
||||
|
{ |
||||
|
class WglPlatformOpenGlInterface : IPlatformOpenGlInterface |
||||
|
{ |
||||
|
public WglContext PrimaryContext { get; } |
||||
|
IGlContext IPlatformOpenGlInterface.PrimaryContext => PrimaryContext; |
||||
|
public IGlContext CreateSharedContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, PrimaryContext); |
||||
|
|
||||
|
public bool CanShareContexts => true; |
||||
|
public bool CanCreateContexts => true; |
||||
|
public IGlContext CreateContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, null); |
||||
|
|
||||
|
private WglPlatformOpenGlInterface(WglContext primary) |
||||
|
{ |
||||
|
PrimaryContext = primary; |
||||
|
} |
||||
|
|
||||
|
public static WglPlatformOpenGlInterface TryCreate() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions(); |
||||
|
var primary = WglDisplay.CreateContext(opts.WglProfiles.ToArray(), null); |
||||
|
return new WglPlatformOpenGlInterface(primary); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("WGL", "Unable to initialize WGL: " + e); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using Avalonia.OpenGL; |
||||
|
using static Avalonia.Win32.Interop.UnmanagedMethods; |
||||
|
|
||||
|
namespace Avalonia.Win32.OpenGl |
||||
|
{ |
||||
|
internal class WglRestoreContext : IDisposable |
||||
|
{ |
||||
|
private readonly object _monitor; |
||||
|
private readonly IntPtr _oldDc; |
||||
|
private readonly IntPtr _oldContext; |
||||
|
|
||||
|
public WglRestoreContext(IntPtr gc, IntPtr context, object monitor, bool takeMonitor = true) |
||||
|
{ |
||||
|
_monitor = monitor; |
||||
|
_oldDc = wglGetCurrentDC(); |
||||
|
_oldContext = wglGetCurrentContext(); |
||||
|
|
||||
|
if (monitor != null && takeMonitor) |
||||
|
Monitor.Enter(monitor); |
||||
|
|
||||
|
if (!wglMakeCurrent(gc, context)) |
||||
|
{ |
||||
|
if(monitor != null && takeMonitor) |
||||
|
Monitor.Exit(monitor); |
||||
|
throw new OpenGlException("Unable to make the context current"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (!wglMakeCurrent(_oldDc, _oldContext)) |
||||
|
wglMakeCurrent(IntPtr.Zero, IntPtr.Zero); |
||||
|
if (_monitor != null) |
||||
|
Monitor.Exit(_monitor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 12 KiB |