diff --git a/tests/SkiaSharp.Desktop.Tests/SkiaSharp.Desktop.Tests.csproj b/tests/SkiaSharp.Desktop.Tests/SkiaSharp.Desktop.Tests.csproj index dc2671e7..4f70b09b 100644 --- a/tests/SkiaSharp.Desktop.Tests/SkiaSharp.Desktop.Tests.csproj +++ b/tests/SkiaSharp.Desktop.Tests/SkiaSharp.Desktop.Tests.csproj @@ -101,14 +101,65 @@ - - CglContext.cs + + GlContexts\Cgl\Cgl.cs - - GlxContext.cs + + GlContexts\Cgl\CglContext.cs - - SKCodecTest.cs + + GlContexts\Cgl\CGLError.cs + + + GlContexts\Cgl\CGLOpenGLProfile.cs + + + GlContexts\Cgl\CGLPixelFormatAttribute.cs + + + GlContexts\GlContext.cs + + + GlContexts\Glx\Glx.cs + + + GlContexts\Glx\GlxContext.cs + + + GlContexts\Glx\Xlib.cs + + + GlContexts\Glx\XVisualClass.cs + + + GlContexts\Glx\XVisualInfo.cs + + + GlContexts\Wgl\Gdi32.cs + + + GlContexts\Wgl\Kernel32.cs + + + GlContexts\Wgl\PIXELFORMATDESCRIPTOR.cs + + + GlContexts\Wgl\RECT.cs + + + GlContexts\Wgl\User32.cs + + + GlContexts\Wgl\Wgl.cs + + + GlContexts\Wgl\WglContext.cs + + + GlContexts\Wgl\WindowStyles.cs + + + GlContexts\Wgl\WNDCLASS.cs GRContextTest.cs @@ -116,46 +167,49 @@ GRGlInterfaceTest.cs - - SKStringTest.cs + + SKBasicTypesTest.cs + + + SKBitmapTest.cs + + + SKCodecTest.cs + + + SKColorTableTest.cs + + + SKColorTest.cs + + + SKDataTest.cs - SKManagedStreamTest.cs - - SKSurfaceTest.cs - - - SKTest.cs - - - SKTypefaceTest.cs - SKPaintTest.cs SKPathTest.cs - - SKColorTest.cs + + SKStringTest.cs - - SKBasicTypesTest.cs - - - SKBitmapTest.cs - - - SKDataTest.cs - - - SKColorTableTest.cs + + SKSurfaceTest.cs SKSvgTest.cs + + SKTest.cs + + + SKTypefaceTest.cs + + diff --git a/tests/SkiaSharp.NetCore.Tests/project.json b/tests/SkiaSharp.NetCore.Tests/project.json index 300b8a09..7b6ba824 100644 --- a/tests/SkiaSharp.NetCore.Tests/project.json +++ b/tests/SkiaSharp.NetCore.Tests/project.json @@ -7,7 +7,7 @@ ], "allowUnsafe": true, "compile": { - "include": "../Tests/*.cs", + "include": "../Tests/**/*.cs", "excludeFiles":[ "../Tests/SKSurfaceTest.cs" ] diff --git a/tests/Tests/CglContext.cs b/tests/Tests/CglContext.cs deleted file mode 100644 index 6520a635..00000000 --- a/tests/Tests/CglContext.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace SkiaSharp.Tests -{ - internal class CglContext : GlContext - { - private IntPtr fContext; - - public CglContext() - { - var attributes = new [] { - CGLPixelFormatAttribute.kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)CGLOpenGLProfile.kCGLOGLPVersion_3_2_Core, - CGLPixelFormatAttribute.kCGLPFADoubleBuffer, - CGLPixelFormatAttribute.kCGLPFANone - }; - - IntPtr pixFormat; - int npix; - - Cgl.CGLChoosePixelFormat(attributes, out pixFormat, out npix); - - if (pixFormat == IntPtr.Zero) { - throw new Exception("CGLChoosePixelFormat failed."); - } - - Cgl.CGLCreateContext(pixFormat, IntPtr.Zero, out fContext); - Cgl.CGLReleasePixelFormat(pixFormat); - - if (fContext == IntPtr.Zero) { - throw new Exception("CGLCreateContext failed."); - } - } - - public override void MakeCurrent() - { - Cgl.CGLSetCurrentContext(fContext); - } - - public override void SwapBuffers() - { - Cgl.CGLFlushDrawable(fContext); - } - - public override void Destroy() - { - if (fContext != IntPtr.Zero) { - Cgl.CGLReleaseContext(fContext); - fContext = IntPtr.Zero; - } - } - } - - internal enum CGLOpenGLProfile { - kCGLOGLPVersion_Legacy = 0x1000, - kCGLOGLPVersion_3_2_Core = 0x3200, - kCGLOGLPVersion_GL3_Core = 0x3200, - kCGLOGLPVersion_GL4_Core = 0x4100, - } - - internal enum CGLPixelFormatAttribute { - kCGLPFANone = 0, - kCGLPFAAllRenderers = 1, - kCGLPFATripleBuffer = 3, - kCGLPFADoubleBuffer = 5, - kCGLPFAColorSize = 8, - kCGLPFAAlphaSize = 11, - kCGLPFADepthSize = 12, - kCGLPFAStencilSize = 13, - kCGLPFAMinimumPolicy = 51, - kCGLPFAMaximumPolicy = 52, - kCGLPFASampleBuffers = 55, - kCGLPFASamples = 56, - kCGLPFAColorFloat = 58, - kCGLPFAMultisample = 59, - kCGLPFASupersample = 60, - kCGLPFASampleAlpha = 61, - kCGLPFARendererID = 70, - kCGLPFANoRecovery = 72, - kCGLPFAAccelerated = 73, - kCGLPFAClosestPolicy = 74, - kCGLPFABackingStore = 76, - kCGLPFABackingVolatile = 77, - kCGLPFADisplayMask = 84, - kCGLPFAAllowOfflineRenderers = 96, - kCGLPFAAcceleratedCompute = 97, - kCGLPFAOpenGLProfile = 99, - kCGLPFASupportsAutomaticGraphicsSwitching = 101, - kCGLPFAVirtualScreenCount = 128, - } - - internal enum CGLError { - kCGLNoError = 0, - kCGLBadAttribute = 10000, - kCGLBadProperty = 10001, - kCGLBadPixelFormat = 10002, - kCGLBadRendererInfo = 10003, - kCGLBadContext = 10004, - kCGLBadDrawable = 10005, - kCGLBadDisplay = 10006, - kCGLBadState = 10007, - kCGLBadValue = 10008, - kCGLBadMatch = 10009, - kCGLBadEnumeration = 10010, - kCGLBadOffScreen = 10011, - kCGLBadFullScreen = 10012, - kCGLBadWindow = 10013, - kCGLBadAddress = 10014, - kCGLBadCodeModule = 10015, - kCGLBadAlloc = 10016, - kCGLBadConnection = 10017, - } - - internal class Cgl - { - private const string libGL = "/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL"; - - [DllImport(libGL)] - public extern static void CGLGetVersion(out int majorvers, out int minorvers); - [DllImport(libGL)] - public extern static CGLError CGLChoosePixelFormat([In] CGLPixelFormatAttribute[] attribs, out IntPtr pix, out int npix); - [DllImport(libGL)] - public extern static CGLError CGLCreateContext(IntPtr pix, IntPtr share, out IntPtr ctx); - [DllImport(libGL)] - public extern static CGLError CGLReleasePixelFormat(IntPtr pix); - [DllImport(libGL)] - public extern static CGLError CGLSetCurrentContext(IntPtr ctx); - [DllImport(libGL)] - public extern static void CGLReleaseContext(IntPtr ctx); - [DllImport(libGL)] - public extern static CGLError CGLFlushDrawable(IntPtr ctx); - } -} diff --git a/tests/Tests/GlContexts/Cgl/CGLError.cs b/tests/Tests/GlContexts/Cgl/CGLError.cs new file mode 100644 index 00000000..83cbc317 --- /dev/null +++ b/tests/Tests/GlContexts/Cgl/CGLError.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal enum CGLError { + kCGLNoError = 0, + kCGLBadAttribute = 10000, + kCGLBadProperty = 10001, + kCGLBadPixelFormat = 10002, + kCGLBadRendererInfo = 10003, + kCGLBadContext = 10004, + kCGLBadDrawable = 10005, + kCGLBadDisplay = 10006, + kCGLBadState = 10007, + kCGLBadValue = 10008, + kCGLBadMatch = 10009, + kCGLBadEnumeration = 10010, + kCGLBadOffScreen = 10011, + kCGLBadFullScreen = 10012, + kCGLBadWindow = 10013, + kCGLBadAddress = 10014, + kCGLBadCodeModule = 10015, + kCGLBadAlloc = 10016, + kCGLBadConnection = 10017, + } +} diff --git a/tests/Tests/GlContexts/Cgl/CGLOpenGLProfile.cs b/tests/Tests/GlContexts/Cgl/CGLOpenGLProfile.cs new file mode 100644 index 00000000..0071d89c --- /dev/null +++ b/tests/Tests/GlContexts/Cgl/CGLOpenGLProfile.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal enum CGLOpenGLProfile { + kCGLOGLPVersion_Legacy = 0x1000, + kCGLOGLPVersion_3_2_Core = 0x3200, + kCGLOGLPVersion_GL3_Core = 0x3200, + kCGLOGLPVersion_GL4_Core = 0x4100, + } +} diff --git a/tests/Tests/GlContexts/Cgl/CGLPixelFormatAttribute.cs b/tests/Tests/GlContexts/Cgl/CGLPixelFormatAttribute.cs new file mode 100644 index 00000000..19bf8004 --- /dev/null +++ b/tests/Tests/GlContexts/Cgl/CGLPixelFormatAttribute.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal enum CGLPixelFormatAttribute { + kCGLPFANone = 0, + kCGLPFAAllRenderers = 1, + kCGLPFATripleBuffer = 3, + kCGLPFADoubleBuffer = 5, + kCGLPFAColorSize = 8, + kCGLPFAAlphaSize = 11, + kCGLPFADepthSize = 12, + kCGLPFAStencilSize = 13, + kCGLPFAMinimumPolicy = 51, + kCGLPFAMaximumPolicy = 52, + kCGLPFASampleBuffers = 55, + kCGLPFASamples = 56, + kCGLPFAColorFloat = 58, + kCGLPFAMultisample = 59, + kCGLPFASupersample = 60, + kCGLPFASampleAlpha = 61, + kCGLPFARendererID = 70, + kCGLPFANoRecovery = 72, + kCGLPFAAccelerated = 73, + kCGLPFAClosestPolicy = 74, + kCGLPFABackingStore = 76, + kCGLPFABackingVolatile = 77, + kCGLPFADisplayMask = 84, + kCGLPFAAllowOfflineRenderers = 96, + kCGLPFAAcceleratedCompute = 97, + kCGLPFAOpenGLProfile = 99, + kCGLPFASupportsAutomaticGraphicsSwitching = 101, + kCGLPFAVirtualScreenCount = 128, + } +} diff --git a/tests/Tests/GlContexts/Cgl/Cgl.cs b/tests/Tests/GlContexts/Cgl/Cgl.cs new file mode 100644 index 00000000..a7f15239 --- /dev/null +++ b/tests/Tests/GlContexts/Cgl/Cgl.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal class Cgl + { + private const string libGL = "/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL"; + + [DllImport(libGL)] + public extern static void CGLGetVersion(out int majorvers, out int minorvers); + [DllImport(libGL)] + public extern static CGLError CGLChoosePixelFormat([In] CGLPixelFormatAttribute[] attribs, out IntPtr pix, out int npix); + [DllImport(libGL)] + public extern static CGLError CGLCreateContext(IntPtr pix, IntPtr share, out IntPtr ctx); + [DllImport(libGL)] + public extern static CGLError CGLReleasePixelFormat(IntPtr pix); + [DllImport(libGL)] + public extern static CGLError CGLSetCurrentContext(IntPtr ctx); + [DllImport(libGL)] + public extern static void CGLReleaseContext(IntPtr ctx); + [DllImport(libGL)] + public extern static CGLError CGLFlushDrawable(IntPtr ctx); + } +} diff --git a/tests/Tests/GlContexts/Cgl/CglContext.cs b/tests/Tests/GlContexts/Cgl/CglContext.cs new file mode 100644 index 00000000..a8697cd4 --- /dev/null +++ b/tests/Tests/GlContexts/Cgl/CglContext.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal class CglContext : GlContext + { + private IntPtr fContext; + + public CglContext() + { + var attributes = new [] { + CGLPixelFormatAttribute.kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)CGLOpenGLProfile.kCGLOGLPVersion_3_2_Core, + CGLPixelFormatAttribute.kCGLPFADoubleBuffer, + CGLPixelFormatAttribute.kCGLPFANone + }; + + IntPtr pixFormat; + int npix; + + Cgl.CGLChoosePixelFormat(attributes, out pixFormat, out npix); + + if (pixFormat == IntPtr.Zero) { + throw new Exception("CGLChoosePixelFormat failed."); + } + + Cgl.CGLCreateContext(pixFormat, IntPtr.Zero, out fContext); + Cgl.CGLReleasePixelFormat(pixFormat); + + if (fContext == IntPtr.Zero) { + throw new Exception("CGLCreateContext failed."); + } + } + + public override void MakeCurrent() + { + Cgl.CGLSetCurrentContext(fContext); + } + + public override void SwapBuffers() + { + Cgl.CGLFlushDrawable(fContext); + } + + public override void Destroy() + { + if (fContext != IntPtr.Zero) { + Cgl.CGLReleaseContext(fContext); + fContext = IntPtr.Zero; + } + } + } +} diff --git a/tests/Tests/GlContexts/GlContext.cs b/tests/Tests/GlContexts/GlContext.cs new file mode 100644 index 00000000..61206f12 --- /dev/null +++ b/tests/Tests/GlContexts/GlContext.cs @@ -0,0 +1,16 @@ +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + public abstract class GlContext : IDisposable + { + public abstract void MakeCurrent(); + public abstract void SwapBuffers(); + public abstract void Destroy(); + + void IDisposable.Dispose() => Destroy(); + } +} diff --git a/tests/Tests/GlContexts/Glx/Glx.cs b/tests/Tests/GlContexts/Glx/Glx.cs new file mode 100644 index 00000000..cad02ac4 --- /dev/null +++ b/tests/Tests/GlContexts/Glx/Glx.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal class Glx + { + private const string libGL = "libGL"; + + public const int GLX_USE_GL = 1; + public const int GLX_BUFFER_SIZE = 2; + public const int GLX_LEVEL = 3; + public const int GLX_RGBA = 4; + public const int GLX_DOUBLEBUFFER = 5; + public const int GLX_STEREO = 6; + public const int GLX_AUX_BUFFERS = 7; + public const int GLX_RED_SIZE = 8; + public const int GLX_GREEN_SIZE = 9; + public const int GLX_BLUE_SIZE = 10; + public const int GLX_ALPHA_SIZE = 11; + public const int GLX_DEPTH_SIZE = 12; + public const int GLX_STENCIL_SIZE = 13; + public const int GLX_ACCUM_RED_SIZE = 14; + public const int GLX_ACCUM_GREEN_SIZE = 15; + public const int GLX_ACCUM_BLUE_SIZE = 16; + public const int GLX_ACCUM_ALPHA_SIZE = 17; + + public const int GLX_DRAWABLE_TYPE = 0x8010; + public const int GLX_RENDER_TYPE = 0x8011; + public const int GLX_X_RENDERABLE = 0x8012; + public const int GLX_RGBA_TYPE = 0x8014; + public const int GLX_COLOR_INDEX_TYPE = 0x8015; + + public const int GLX_PIXMAP_BIT = 0x00000002; + + public const int GLX_RGBA_BIT = 0x00000001; + + public const int GLX_SAMPLE_BUFFERS = 0x186a0; + public const int GLX_SAMPLES = 0x186a1; + + public const int GLX_CONTEXT_DEBUG_BIT_ARB = 0x00000001; + public const int GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x00000002; + public const int GLX_CONTEXT_MAJOR_VERSION_ARB = 0x2091; + public const int GLX_CONTEXT_MINOR_VERSION_ARB = 0x2092; + public const int GLX_CONTEXT_FLAGS_ARB = 0x2094; + + public const int GLX_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001; + public const int GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002; + public const int GLX_CONTEXT_PROFILE_MASK_ARB = 0x9126; + + static Glx() + { + var ptr = glXGetProcAddressARB("glXCreateContextAttribsARB"); + if (ptr != IntPtr.Zero) { + glXCreateContextAttribsARB = (glXCreateContextAttribsARBDelegate)Marshal.GetDelegateForFunctionPointer(ptr, typeof(glXCreateContextAttribsARBDelegate)); + } + } + + [DllImport(libGL)] + public extern static bool glXQueryVersion(IntPtr dpy, out int maj, out int min); + [DllImport(libGL)] + public extern static IntPtr glXChooseFBConfig(IntPtr dpy, int screen, [In] int[] attribList, out int nitems); + public static IntPtr[] ChooseFBConfig(IntPtr dpy, int screen, int[] attribList) + { + int nitems; + var fbcArrayPtr = glXChooseFBConfig(dpy, screen, attribList, out nitems); + + var fbcArray = new IntPtr[nitems]; + Marshal.Copy(fbcArrayPtr, fbcArray, 0, nitems); + + Xlib.XFree(fbcArrayPtr); + + return fbcArray; + } + [DllImport(libGL)] + public extern static IntPtr glXGetVisualFromFBConfig(IntPtr dpy, IntPtr config); + public static XVisualInfo GetVisualFromFBConfig(IntPtr dpy, IntPtr config) + { + var visualPtr = glXGetVisualFromFBConfig(dpy, config); + if (visualPtr == IntPtr.Zero) { + throw new Exception("Failed to retrieve visual from framebuffer config."); + } + + var visual = (XVisualInfo) Marshal.PtrToStructure(visualPtr, typeof(XVisualInfo)); + + Xlib.XFree(visualPtr); + + return visual; + } + [DllImport(libGL)] + public extern static bool glXMakeCurrent(IntPtr dpy, IntPtr drawable, IntPtr ctx); + [DllImport(libGL)] + public extern static bool glXSwapBuffers(IntPtr dpy, IntPtr drawable); + [DllImport(libGL)] + public extern static bool glXIsDirect(IntPtr dpy, IntPtr ctx); + [DllImport(libGL)] + public extern static int glXGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); + [DllImport(libGL)] + public extern static IntPtr glXCreateGLXPixmap(IntPtr dpy, ref XVisualInfo visual, IntPtr pixmap); + [DllImport(libGL)] + public extern static void glXDestroyGLXPixmap(IntPtr dpy, IntPtr pixmap); + [DllImport(libGL)] + public extern static void glXDestroyContext(IntPtr dpy, IntPtr ctx); + [DllImport(libGL)] + public extern static IntPtr glXQueryExtensionsString(IntPtr dpy, int screen); + public static string QueryExtensionsString(IntPtr dpy, int screen) + { + return Marshal.PtrToStringAnsi(glXQueryExtensionsString(dpy, screen)); + } + public static string[] QueryExtensions(IntPtr dpy, int screen) + { + var str = QueryExtensionsString(dpy, screen); + if (string.IsNullOrEmpty(str)) { + return new string[0]; + } + return str.Split(' '); + } + [DllImport(libGL)] + public extern static IntPtr glXGetProcAddressARB(string procname); + [DllImport(libGL)] + public extern static IntPtr glXCreateNewContext(IntPtr dpy, IntPtr config, int renderType, IntPtr shareList, int direct); + public static readonly glXCreateContextAttribsARBDelegate glXCreateContextAttribsARB; + public delegate IntPtr glXCreateContextAttribsARBDelegate(IntPtr dpy, IntPtr config, IntPtr share_context, int direct, int[] attrib_list); + } +} diff --git a/tests/Tests/GlContexts/Glx/GlxContext.cs b/tests/Tests/GlContexts/Glx/GlxContext.cs new file mode 100644 index 00000000..b46aa358 --- /dev/null +++ b/tests/Tests/GlContexts/Glx/GlxContext.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal class GlxContext : GlContext + { + private IntPtr fDisplay; + private IntPtr fPixmap; + private IntPtr fGlxPixmap; + private IntPtr fContext; + + public GlxContext() + { + fDisplay = Xlib.XOpenDisplay(null); + if (fDisplay == IntPtr.Zero) { + Destroy(); + throw new Exception("Failed to open X display."); + } + + var visualAttribs = new [] { + Glx.GLX_X_RENDERABLE, Xlib.True, + Glx.GLX_DRAWABLE_TYPE, Glx.GLX_PIXMAP_BIT, + Glx.GLX_RENDER_TYPE, Glx.GLX_RGBA_BIT, + // Glx.GLX_DOUBLEBUFFER, Xlib.True, + Glx.GLX_RED_SIZE, 8, + Glx.GLX_GREEN_SIZE, 8, + Glx.GLX_BLUE_SIZE, 8, + Glx.GLX_ALPHA_SIZE, 8, + Glx.GLX_DEPTH_SIZE, 24, + Glx.GLX_STENCIL_SIZE, 8, + // Glx.GLX_SAMPLE_BUFFERS, 1, + // Glx.GLX_SAMPLES, 4, + Xlib.None + }; + + int glxMajor, glxMinor; + + if (!Glx.glXQueryVersion(fDisplay, out glxMajor, out glxMinor) || + (glxMajor < 1) || + (glxMajor == 1 && glxMinor < 3)) { + Destroy(); + throw new Exception("GLX version 1.3 or higher required."); + } + + var fbc = Glx.ChooseFBConfig(fDisplay, Xlib.XDefaultScreen(fDisplay), visualAttribs); + if (fbc.Length == 0) { + Destroy(); + throw new Exception("Failed to retrieve a framebuffer config."); + } + + var bestFBC = IntPtr.Zero; + var bestNumSamp = -1; + for (int i = 0; i < fbc.Length; i++) { + + int sampleBuf, samples; + Glx.glXGetFBConfigAttrib(fDisplay, fbc[i], Glx.GLX_SAMPLE_BUFFERS, out sampleBuf); + Glx.glXGetFBConfigAttrib(fDisplay, fbc[i], Glx.GLX_SAMPLES, out samples); + + if (bestFBC == IntPtr.Zero || (sampleBuf > 0 && samples > bestNumSamp)) { + bestFBC = fbc[i]; + bestNumSamp = samples; + } + } + var vi = Glx.GetVisualFromFBConfig(fDisplay, bestFBC); + + fPixmap = Xlib.XCreatePixmap(fDisplay, Xlib.XRootWindow(fDisplay, vi.screen), 10, 10, (uint)vi.depth); + if (fPixmap == IntPtr.Zero) { + Destroy(); + throw new Exception("Failed to create pixmap."); + } + + fGlxPixmap = Glx.glXCreateGLXPixmap(fDisplay, ref vi, fPixmap); + + var glxExts = Glx.QueryExtensions(fDisplay, Xlib.XDefaultScreen(fDisplay)); + if (Array.IndexOf(glxExts, "GLX_ARB_create_context") == -1 || + Glx.glXCreateContextAttribsARB == null) { + Console.WriteLine("OpenGL 3.0 doesn't seem to be available."); + fContext = Glx.glXCreateNewContext(fDisplay, bestFBC, Glx.GLX_RGBA_TYPE, IntPtr.Zero, Xlib.True); + } else { + // Let's just use OpenGL 3.0, but we could try find the highest + int major = 3, minor = 0; + var flags = new List { + Glx.GLX_CONTEXT_MAJOR_VERSION_ARB, major, + Glx.GLX_CONTEXT_MINOR_VERSION_ARB, minor, + }; + if (major > 2) { + flags.AddRange(new[] { + Glx.GLX_CONTEXT_PROFILE_MASK_ARB, Glx.GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + }); + } + flags.Add(Xlib.None); + + fContext = Glx.glXCreateContextAttribsARB(fDisplay, bestFBC, IntPtr.Zero, Xlib.True, flags.ToArray()); + } + if (fContext == IntPtr.Zero) { + Destroy(); + throw new Exception("Failed to create an OpenGL context."); + } + + if (!Glx.glXIsDirect(fDisplay, fContext)) { + Console.WriteLine("Obtained indirect GLX rendering context."); + } + } + + public override void MakeCurrent() + { + if (!Glx.glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { + Destroy(); + throw new Exception("Failed to set the context."); + } + } + + public override void SwapBuffers() + { + Glx.glXSwapBuffers(fDisplay, fGlxPixmap); + } + + public override void Destroy() + { + if (fDisplay != IntPtr.Zero) { + Glx.glXMakeCurrent(fDisplay, IntPtr.Zero, IntPtr.Zero); + + if (fContext != IntPtr.Zero) { + Glx.glXDestroyContext(fDisplay, fContext); + fContext = IntPtr.Zero; + } + + if (fGlxPixmap != IntPtr.Zero) { + Glx.glXDestroyGLXPixmap(fDisplay, fGlxPixmap); + fGlxPixmap = IntPtr.Zero; + } + + if (fPixmap != IntPtr.Zero) { + Xlib.XFreePixmap(fDisplay, fPixmap); + fPixmap = IntPtr.Zero; + } + + fDisplay = IntPtr.Zero; + } + } + } +} diff --git a/tests/Tests/GlContexts/Glx/XVisualClass.cs b/tests/Tests/GlContexts/Glx/XVisualClass.cs new file mode 100644 index 00000000..85dd6681 --- /dev/null +++ b/tests/Tests/GlContexts/Glx/XVisualClass.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal enum XVisualClass : int { + StaticGray = 0, + GrayScale = 1, + StaticColor = 2, + PseudoColor = 3, + TrueColor = 4, + DirectColor = 5 + } +} diff --git a/tests/Tests/GlContexts/Glx/XVisualInfo.cs b/tests/Tests/GlContexts/Glx/XVisualInfo.cs new file mode 100644 index 00000000..b1ed544a --- /dev/null +++ b/tests/Tests/GlContexts/Glx/XVisualInfo.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + [StructLayout(LayoutKind.Sequential)] + internal struct XVisualInfo + { + public IntPtr visual; + public IntPtr visualid; + public int screen; + public int depth; + public XVisualClass c_class; + public ulong red_mask; + public ulong green_mask; + public ulong blue_mask; + public int colormap_size; + public int bits_per_rgb; + } +} diff --git a/tests/Tests/GlContexts/Glx/Xlib.cs b/tests/Tests/GlContexts/Glx/Xlib.cs new file mode 100644 index 00000000..b7511360 --- /dev/null +++ b/tests/Tests/GlContexts/Glx/Xlib.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal class Xlib + { + private const string libX11 = "libX11"; + + public const int None = 0; + public const int True = 1; + public const int False = 0; + + [DllImport(libX11)] + public extern static IntPtr XOpenDisplay(string display_name); + [DllImport(libX11)] + public extern static int XFree(IntPtr data); + [DllImport(libX11)] + public extern static int XDefaultScreen(IntPtr display); + [DllImport(libX11)] + public extern static IntPtr XRootWindow(IntPtr display, int screen); + [DllImport(libX11)] + public extern static IntPtr XCreatePixmap(IntPtr display, IntPtr d, uint width, uint height, uint depth); + [DllImport(libX11)] + public extern static IntPtr XFreePixmap(IntPtr display, IntPtr pixmap); + } +} diff --git a/tests/Tests/GlContexts/Wgl/Gdi32.cs b/tests/Tests/GlContexts/Wgl/Gdi32.cs new file mode 100644 index 00000000..7708a65e --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/Gdi32.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal class Gdi32 + { + private const string gdi32 = "gdi32.dll"; + + public const byte PFD_TYPE_RGBA = 0; + + public const byte PFD_MAIN_PLANE = 0; + + public const uint PFD_DRAW_TO_WINDOW = 0x00000004; + public const uint PFD_SUPPORT_OPENGL = 0x00000020; + + [DllImport(gdi32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetPixelFormat(IntPtr hdc, int iPixelFormat, [In] ref PIXELFORMATDESCRIPTOR ppfd); + + [DllImport(gdi32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + public static extern int ChoosePixelFormat(IntPtr hdc, [In] ref PIXELFORMATDESCRIPTOR ppfd); + + [DllImport(gdi32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SwapBuffers(IntPtr hdc); + } +} diff --git a/tests/Tests/GlContexts/Wgl/Kernel32.cs b/tests/Tests/GlContexts/Wgl/Kernel32.cs new file mode 100644 index 00000000..4e2849b4 --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/Kernel32.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal class Kernel32 + { + private const string kernel32 = "kernel32.dll"; + + static Kernel32() + { + CurrentModuleHandle = Kernel32.GetModuleHandle(null); + if (CurrentModuleHandle == IntPtr.Zero) + { + throw new Exception("Could not get module handle."); + } + } + + public static IntPtr CurrentModuleHandle { get; } + + [DllImport(kernel32, CallingConvention = CallingConvention.Winapi, BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern IntPtr GetModuleHandle([MarshalAs(UnmanagedType.LPTStr)] string lpModuleName); + } +} diff --git a/tests/Tests/GlContexts/Wgl/PIXELFORMATDESCRIPTOR.cs b/tests/Tests/GlContexts/Wgl/PIXELFORMATDESCRIPTOR.cs new file mode 100644 index 00000000..2a024c3c --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/PIXELFORMATDESCRIPTOR.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + [StructLayout(LayoutKind.Sequential)] + internal struct PIXELFORMATDESCRIPTOR + { + public ushort nSize; + public ushort nVersion; + public uint dwFlags; + public byte iPixelType; + public byte cColorBits; + public byte cRedBits; + public byte cRedShift; + public byte cGreenBits; + public byte cGreenShift; + public byte cBlueBits; + public byte cBlueShift; + public byte cAlphaBits; + public byte cAlphaShift; + public byte cAccumBits; + public byte cAccumRedBits; + public byte cAccumGreenBits; + public byte cAccumBlueBits; + public byte cAccumAlphaBits; + public byte cDepthBits; + public byte cStencilBits; + public byte cAuxBuffers; + public byte iLayerType; + public byte bReserved; + public int dwLayerMask; + public int dwVisibleMask; + public int dwDamageMask; + } +} diff --git a/tests/Tests/GlContexts/Wgl/RECT.cs b/tests/Tests/GlContexts/Wgl/RECT.cs new file mode 100644 index 00000000..5915e4fc --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/RECT.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + [StructLayout(LayoutKind.Sequential)] + internal struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + } +} diff --git a/tests/Tests/GlContexts/Wgl/User32.cs b/tests/Tests/GlContexts/Wgl/User32.cs new file mode 100644 index 00000000..0eec6d92 --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/User32.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace SkiaSharp.Tests +{ + internal class User32 + { + private const string user32 = "user32.dll"; + + public const uint IDC_ARROW = 32512; + + public const uint IDI_APPLICATION = 32512; + public const uint IDI_WINLOGO = 32517; + + public const int SW_HIDE = 0; + + public const uint CS_VREDRAW = 0x1; + public const uint CS_HREDRAW = 0x2; + public const uint CS_OWNDC = 0x20; + + public const uint WS_EX_CLIENTEDGE = 0x00000200; + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ushort RegisterClass(ref WNDCLASS lpWndClass); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ushort UnregisterClass([MarshalAs(UnmanagedType.LPTStr)] string lpClassName, IntPtr hInstance); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + public static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern IntPtr CreateWindowEx(uint dwExStyle, [MarshalAs(UnmanagedType.LPTStr)] string lpClassName, [MarshalAs(UnmanagedType.LPTStr)] string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam); + + public static IntPtr CreateWindow(string lpClassName, string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam) + { + return CreateWindowEx(0, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); + } + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + public static extern IntPtr GetDC(IntPtr hWnd); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DestroyWindow(IntPtr hWnd); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsWindow(IntPtr hWnd); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AdjustWindowRectEx(ref RECT lpRect, WindowStyles dwStyle, bool bMenu, uint dwExStyle); + + [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow); + } +} diff --git a/tests/Tests/GlContexts/Wgl/WNDCLASS.cs b/tests/Tests/GlContexts/Wgl/WNDCLASS.cs new file mode 100644 index 00000000..a08db544 --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/WNDCLASS.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal delegate IntPtr WNDPROC(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + [StructLayout(LayoutKind.Sequential)] + internal struct WNDCLASS + { + public uint style; + [MarshalAs(UnmanagedType.FunctionPtr)] + public WNDPROC lpfnWndProc; + public int cbClsExtra; + public int cbWndExtra; + public IntPtr hInstance; + public IntPtr hIcon; + public IntPtr hCursor; + public IntPtr hbrBackground; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpszMenuName; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpszClassName; + } +} diff --git a/tests/Tests/GlContexts/Wgl/Wgl.cs b/tests/Tests/GlContexts/Wgl/Wgl.cs new file mode 100644 index 00000000..c601df8b --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/Wgl.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + internal class Wgl + { + private const string opengl32 = "opengl32.dll"; + + public const int NONE = 0; + public const int FALSE = 0; + public const int TRUE = 1; + + 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; + + static Wgl() + { + // save the current GL context + var prevDC = Wgl.wglGetCurrentDC(); + var prevGLRC = Wgl.wglGetCurrentContext(); + + // register the dummy window class + var wc = new WNDCLASS + { + style = (User32.CS_HREDRAW | User32.CS_VREDRAW | User32.CS_OWNDC), + lpfnWndProc = (WNDPROC)User32.DefWindowProc, + cbClsExtra = 0, + cbWndExtra = 0, + hInstance = Kernel32.CurrentModuleHandle, + hCursor = User32.LoadCursor(IntPtr.Zero, (int)User32.IDC_ARROW), + hIcon = User32.LoadIcon(IntPtr.Zero, (IntPtr)User32.IDI_WINLOGO), + hbrBackground = IntPtr.Zero, + lpszMenuName = null, + lpszClassName = "DummyClass" + }; + if (User32.RegisterClass(ref wc) == 0) + { + throw new Exception("Could not register dummy class."); + } + + // get the the dummy window bounds + var windowRect = new RECT { left = 0, right = 8, top = 0, bottom = 8 }; + User32.AdjustWindowRectEx(ref windowRect, WindowStyles.WS_SYSMENU, false, User32.WS_EX_CLIENTEDGE); + + // create the dummy window + var dummyWND = User32.CreateWindowEx( + User32.WS_EX_CLIENTEDGE, + "DummyClass", + "DummyWindow", + WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN | WindowStyles.WS_SYSMENU, + 0, 0, + windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, + IntPtr.Zero, IntPtr.Zero, Kernel32.CurrentModuleHandle, IntPtr.Zero); + if (dummyWND == IntPtr.Zero) + { + User32.UnregisterClass("DummyClass", Kernel32.CurrentModuleHandle); + throw new Exception("Could not create dummy window."); + } + + // get the dummy DC + var dummyDC = User32.GetDC(dummyWND); + + // get the dummy pixel format + var dummyPFD = new PIXELFORMATDESCRIPTOR(); + dummyPFD.nSize = (ushort)Marshal.SizeOf(dummyPFD); + dummyPFD.nVersion = 1; + dummyPFD.dwFlags = Gdi32.PFD_DRAW_TO_WINDOW | Gdi32.PFD_SUPPORT_OPENGL; + dummyPFD.iPixelType = Gdi32.PFD_TYPE_RGBA; + dummyPFD.cColorBits = 32; + dummyPFD.cDepthBits = 24; + dummyPFD.cStencilBits = 8; + dummyPFD.iLayerType = Gdi32.PFD_MAIN_PLANE; + var dummyFormat = Gdi32.ChoosePixelFormat(dummyDC, ref dummyPFD); + Gdi32.SetPixelFormat(dummyDC, dummyFormat, ref dummyPFD); + + // get the dummy GL context + var dummyGLRC = Wgl.wglCreateContext(dummyDC); + if (dummyGLRC == IntPtr.Zero) + { + throw new Exception("Could not create dummy GL context."); + } + Wgl.wglMakeCurrent(dummyDC, dummyGLRC); + + // get the extension methods using the dummy context + wglGetExtensionsStringARB = Wgl.wglGetProcAddress("wglGetExtensionsStringARB"); + wglChoosePixelFormatARB = Wgl.wglGetProcAddress("wglChoosePixelFormatARB"); + wglCreatePbufferARB = Wgl.wglGetProcAddress("wglCreatePbufferARB"); + wglDestroyPbufferARB = Wgl.wglGetProcAddress("wglDestroyPbufferARB"); + wglGetPbufferDCARB = Wgl.wglGetProcAddress("wglGetPbufferDCARB"); + wglReleasePbufferDCARB = Wgl.wglGetProcAddress("wglReleasePbufferDCARB"); + wglSwapIntervalEXT = Wgl.wglGetProcAddress("wglSwapIntervalEXT"); + // GET_PROC(ChoosePixelFormat, ARB); + // GET_PROC(GetPixelFormatAttribiv, ARB); + // GET_PROC(GetPixelFormatAttribfv, ARB); + // GET_PROC(CreateContextAttribs, ARB); + + // destroy the dummy GL context + Wgl.wglMakeCurrent(dummyDC, IntPtr.Zero); + Wgl.wglDeleteContext(dummyGLRC); + + // destroy the dummy window + User32.DestroyWindow(dummyWND); + User32.UnregisterClass("DummyClass", Kernel32.CurrentModuleHandle); + + // reset the initial GL context + Wgl.wglMakeCurrent(prevDC, prevGLRC); + } + + public static bool HasExtension(IntPtr dc, string ext) + { + if (ext == "WGL_ARB_extensions_string") + { + return true; + } + + return Array.IndexOf(GetExtensionsARB(dc), ext) != -1; + } + + public static string GetExtensionsStringARB(IntPtr dc) + { + return Marshal.PtrToStringAnsi(wglGetExtensionsStringARB(dc)); + } + + public static string[] GetExtensionsARB(IntPtr dc) + { + var str = GetExtensionsStringARB(dc); + if (string.IsNullOrEmpty(str)) + { + return new string[0]; + } + return str.Split(' '); + } + + public static readonly wglGetExtensionsStringARBDelegate wglGetExtensionsStringARB; + public static readonly wglChoosePixelFormatARBDelegate wglChoosePixelFormatARB; + public static readonly wglCreatePbufferARBDelegate wglCreatePbufferARB; + public static readonly wglDestroyPbufferARBDelegate wglDestroyPbufferARB; + public static readonly wglGetPbufferDCARBDelegate wglGetPbufferDCARB; + public static readonly wglReleasePbufferDCARBDelegate wglReleasePbufferDCARB; + public static readonly wglSwapIntervalEXTDelegate wglSwapIntervalEXT; + + [DllImport(opengl32, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr wglGetCurrentDC(); + + [DllImport(opengl32, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr wglGetCurrentContext(); + + [DllImport(opengl32, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr wglCreateContext(IntPtr hDC); + + [DllImport(opengl32, CallingConvention = CallingConvention.Winapi)] + public static extern bool wglMakeCurrent(IntPtr hDC, IntPtr hRC); + + [DllImport(opengl32, CallingConvention = CallingConvention.Winapi)] + public static extern bool wglDeleteContext(IntPtr hRC); + + [DllImport(opengl32, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr wglGetProcAddress([MarshalAs(UnmanagedType.LPStr)] string lpszProc); + + public static T wglGetProcAddress(string lpszProc) + { + var ptr = wglGetProcAddress(lpszProc); + if (ptr == IntPtr.Zero) + { + throw new Exception("Unable to load proc: " + lpszProc); + } + return (T)(object)Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)); + } + } + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate IntPtr wglGetExtensionsStringARBDelegate(IntPtr dc); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + public delegate bool wglChoosePixelFormatARBDelegate( + IntPtr dc, + [In] int[] attribIList, + [In] float[] attribFList, + uint maxFormats, + [Out] int[] pixelFormats, + out uint numFormats); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate IntPtr wglCreatePbufferARBDelegate(IntPtr dc, int pixelFormat, int width, int height, [In] int[] attribList); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + public delegate bool wglDestroyPbufferARBDelegate(IntPtr pbuffer); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate IntPtr wglGetPbufferDCARBDelegate(IntPtr pbuffer); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate int wglReleasePbufferDCARBDelegate(IntPtr pbuffer, IntPtr dc); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + public delegate bool wglSwapIntervalEXTDelegate(int interval); +} diff --git a/tests/Tests/GlContexts/Wgl/WglContext.cs b/tests/Tests/GlContexts/Wgl/WglContext.cs new file mode 100644 index 00000000..3a3e5889 --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/WglContext.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace SkiaSharp.Tests +{ + internal class WglContext : GlContext + { + private ushort gWC; + + private IntPtr fWindow; + private IntPtr fDeviceContext; + + private IntPtr fPbuffer; + private IntPtr fPbufferDC; + private IntPtr fPbufferGlContext; + + public WglContext() + { + var wc = new WNDCLASS + { + cbClsExtra = 0, + cbWndExtra = 0, + hbrBackground = IntPtr.Zero, + hCursor = User32.LoadCursor(IntPtr.Zero, (int)User32.IDC_ARROW), + hIcon = User32.LoadIcon(IntPtr.Zero, (IntPtr)User32.IDI_APPLICATION), + hInstance = Kernel32.CurrentModuleHandle, + lpfnWndProc = (WNDPROC)User32.DefWindowProc, + lpszClassName = "Griffin", + lpszMenuName = null, + style = User32.CS_HREDRAW | User32.CS_VREDRAW | User32.CS_OWNDC + }; + + gWC = User32.RegisterClass(ref wc); + if (gWC == 0) + { + throw new Exception("Could not register window class."); + } + + fWindow = User32.CreateWindow( + "Griffin", + "The Invisible Man", + WindowStyles.WS_OVERLAPPEDWINDOW, + 0, 0, + 1, 1, + IntPtr.Zero, IntPtr.Zero, Kernel32.CurrentModuleHandle, IntPtr.Zero); + if (fWindow == IntPtr.Zero) + { + throw new Exception($"Could not create window."); + } + + fDeviceContext = User32.GetDC(fWindow); + if (fDeviceContext == IntPtr.Zero) + { + Destroy(); + throw new Exception("Could not get device context."); + } + + if (!Wgl.HasExtension(fDeviceContext, "WGL_ARB_pixel_format") || + !Wgl.HasExtension(fDeviceContext, "WGL_ARB_pbuffer")) + { + Destroy(); + throw new Exception("DC does not have extensions."); + } + + var iAttrs = new int[] + { + Wgl.WGL_ACCELERATION_ARB, Wgl.WGL_FULL_ACCELERATION_ARB, + Wgl.WGL_DRAW_TO_WINDOW_ARB, Wgl.TRUE, + //Wgl.WGL_DOUBLE_BUFFER_ARB, (doubleBuffered ? TRUE : FALSE), + Wgl.WGL_SUPPORT_OPENGL_ARB, Wgl.TRUE, + Wgl.WGL_RED_BITS_ARB, 8, + Wgl.WGL_GREEN_BITS_ARB, 8, + Wgl.WGL_BLUE_BITS_ARB, 8, + Wgl.WGL_ALPHA_BITS_ARB, 8, + Wgl.WGL_STENCIL_BITS_ARB, 8, + Wgl.NONE, Wgl.NONE + }; + var piFormats = new int[1]; + uint nFormats; + Wgl.wglChoosePixelFormatARB(fDeviceContext, iAttrs, null, (uint)piFormats.Length, piFormats, out nFormats); + if (nFormats == 0) + { + Destroy(); + throw new Exception("Could not get pixel formats."); + } + + fPbuffer = Wgl.wglCreatePbufferARB(fDeviceContext, piFormats[0], 1, 1, null); + if (fPbuffer == IntPtr.Zero) + { + Destroy(); + throw new Exception("Could not create Pbuffer."); + } + + fPbufferDC = Wgl.wglGetPbufferDCARB(fPbuffer); + if (fPbufferDC == IntPtr.Zero) + { + Destroy(); + throw new Exception("Could not get Pbuffer DC."); + } + + var prevDC = Wgl.wglGetCurrentDC(); + var prevGLRC = Wgl.wglGetCurrentContext(); + + fPbufferGlContext = Wgl.wglCreateContext(fPbufferDC); + + Wgl.wglMakeCurrent(prevDC, prevGLRC); + + if (fPbufferGlContext == IntPtr.Zero) + { + Destroy(); + throw new Exception("Could not creeate Pbuffer GL context."); + } + } + + public override void MakeCurrent() + { + if (!Wgl.wglMakeCurrent(fPbufferDC, fPbufferGlContext)) + { + Destroy(); + throw new Exception("Could not set the context."); + } + } + + public override void SwapBuffers() + { + if (!Gdi32.SwapBuffers(fPbufferDC)) + { + Destroy(); + throw new Exception("Could not complete SwapBuffers."); + } + } + + public override void Destroy() + { + if (!Wgl.HasExtension(fPbufferDC, "WGL_ARB_pbuffer")) + { + // ASSERT + } + + Wgl.wglDeleteContext(fPbufferGlContext); + + Wgl.wglReleasePbufferDCARB(fPbuffer, fPbufferDC); + + Wgl.wglDestroyPbufferARB(fPbuffer); + + if (fWindow != IntPtr.Zero) + { + if (fDeviceContext != IntPtr.Zero) + { + User32.ReleaseDC(fWindow, fDeviceContext); + fDeviceContext = IntPtr.Zero; + } + + User32.DestroyWindow(fWindow); + fWindow = IntPtr.Zero; + } + + User32.UnregisterClass("Griffin", Kernel32.CurrentModuleHandle); + } + } +} diff --git a/tests/Tests/GlContexts/Wgl/WindowStyles.cs b/tests/Tests/GlContexts/Wgl/WindowStyles.cs new file mode 100644 index 00000000..a8f02cbb --- /dev/null +++ b/tests/Tests/GlContexts/Wgl/WindowStyles.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SkiaSharp.Tests +{ + [Flags] + internal enum WindowStyles : uint + { + WS_BORDER = 0x800000, + WS_CAPTION = 0xc00000, + WS_CHILD = 0x40000000, + WS_CLIPCHILDREN = 0x2000000, + WS_CLIPSIBLINGS = 0x4000000, + WS_DISABLED = 0x8000000, + WS_DLGFRAME = 0x400000, + WS_GROUP = 0x20000, + WS_HSCROLL = 0x100000, + WS_MAXIMIZE = 0x1000000, + WS_MAXIMIZEBOX = 0x10000, + WS_MINIMIZE = 0x20000000, + WS_MINIMIZEBOX = 0x20000, + WS_OVERLAPPED = 0x0, + WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_SIZEFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, + WS_POPUP = 0x80000000u, + WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, + WS_SIZEFRAME = 0x40000, + WS_SYSMENU = 0x80000, + WS_TABSTOP = 0x10000, + WS_VISIBLE = 0x10000000, + WS_VSCROLL = 0x200000 + } +} diff --git a/tests/Tests/GlxContext.cs b/tests/Tests/GlxContext.cs deleted file mode 100644 index f03184ec..00000000 --- a/tests/Tests/GlxContext.cs +++ /dev/null @@ -1,327 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace SkiaSharp.Tests -{ - internal class GlxContext : GlContext - { - private IntPtr fDisplay; - private IntPtr fPixmap; - private IntPtr fGlxPixmap; - private IntPtr fContext; - - public GlxContext() - { - fDisplay = Xlib.XOpenDisplay(null); - if (fDisplay == IntPtr.Zero) { - Destroy(); - throw new Exception("Failed to open X display."); - } - - var visualAttribs = new [] { - Glx.GLX_X_RENDERABLE, Xlib.True, - Glx.GLX_DRAWABLE_TYPE, Glx.GLX_PIXMAP_BIT, - Glx.GLX_RENDER_TYPE, Glx.GLX_RGBA_BIT, - // Glx.GLX_DOUBLEBUFFER, Xlib.True, - Glx.GLX_RED_SIZE, 8, - Glx.GLX_GREEN_SIZE, 8, - Glx.GLX_BLUE_SIZE, 8, - Glx.GLX_ALPHA_SIZE, 8, - Glx.GLX_DEPTH_SIZE, 24, - Glx.GLX_STENCIL_SIZE, 8, - // Glx.GLX_SAMPLE_BUFFERS, 1, - // Glx.GLX_SAMPLES, 4, - Xlib.None - }; - - int glxMajor, glxMinor; - - if (!Glx.glXQueryVersion(fDisplay, out glxMajor, out glxMinor) || - (glxMajor < 1) || - (glxMajor == 1 && glxMinor < 3)) { - Destroy(); - throw new Exception("GLX version 1.3 or higher required."); - } - - var fbc = Glx.ChooseFBConfig(fDisplay, Xlib.XDefaultScreen(fDisplay), visualAttribs); - if (fbc.Length == 0) { - Destroy(); - throw new Exception("Failed to retrieve a framebuffer config."); - } - - var bestFBC = IntPtr.Zero; - var bestNumSamp = -1; - for (int i = 0; i < fbc.Length; i++) { - - int sampleBuf, samples; - Glx.glXGetFBConfigAttrib(fDisplay, fbc[i], Glx.GLX_SAMPLE_BUFFERS, out sampleBuf); - Glx.glXGetFBConfigAttrib(fDisplay, fbc[i], Glx.GLX_SAMPLES, out samples); - - if (bestFBC == IntPtr.Zero || (sampleBuf > 0 && samples > bestNumSamp)) { - bestFBC = fbc[i]; - bestNumSamp = samples; - } - } - var vi = Glx.GetVisualFromFBConfig(fDisplay, bestFBC); - - fPixmap = Xlib.XCreatePixmap(fDisplay, Xlib.XRootWindow(fDisplay, vi.screen), 10, 10, (uint)vi.depth); - if (fPixmap == IntPtr.Zero) { - Destroy(); - throw new Exception("Failed to create pixmap."); - } - - fGlxPixmap = Glx.glXCreateGLXPixmap(fDisplay, ref vi, fPixmap); - - var glxExts = Glx.QueryExtensions(fDisplay, Xlib.XDefaultScreen(fDisplay)); - if (Array.IndexOf(glxExts, "GLX_ARB_create_context") == -1 || - Glx.glXCreateContextAttribsARB == null) { - Console.WriteLine("OpenGL 3.0 doesn't seem to be available."); - fContext = Glx.glXCreateNewContext(fDisplay, bestFBC, Glx.GLX_RGBA_TYPE, IntPtr.Zero, Xlib.True); - } else { - // Let's just use OpenGL 3.0, but we could try find the highest - int major = 3, minor = 0; - var flags = new List { - Glx.GLX_CONTEXT_MAJOR_VERSION_ARB, major, - Glx.GLX_CONTEXT_MINOR_VERSION_ARB, minor, - }; - if (major > 2) { - flags.AddRange(new[] { - Glx.GLX_CONTEXT_PROFILE_MASK_ARB, Glx.GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - }); - } - flags.Add(Xlib.None); - - fContext = Glx.glXCreateContextAttribsARB(fDisplay, bestFBC, IntPtr.Zero, Xlib.True, flags.ToArray()); - } - if (fContext == IntPtr.Zero) { - Destroy(); - throw new Exception("Failed to create an OpenGL context."); - } - - if (!Glx.glXIsDirect(fDisplay, fContext)) { - Console.WriteLine("Obtained indirect GLX rendering context."); - } - } - - public override void MakeCurrent() - { - if (!Glx.glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { - Destroy(); - throw new Exception("Failed to set the context."); - } - } - - public override void SwapBuffers() - { - Glx.glXSwapBuffers(fDisplay, fGlxPixmap); - } - - public override void Destroy() - { - if (fDisplay != IntPtr.Zero) { - Glx.glXMakeCurrent(fDisplay, IntPtr.Zero, IntPtr.Zero); - - if (fContext != IntPtr.Zero) { - Glx.glXDestroyContext(fDisplay, fContext); - fContext = IntPtr.Zero; - } - - if (fGlxPixmap != IntPtr.Zero) { - Glx.glXDestroyGLXPixmap(fDisplay, fGlxPixmap); - fGlxPixmap = IntPtr.Zero; - } - - if (fPixmap != IntPtr.Zero) { - Xlib.XFreePixmap(fDisplay, fPixmap); - fPixmap = IntPtr.Zero; - } - - fDisplay = IntPtr.Zero; - } - } - } - - internal class Xlib - { - private const string libX11 = "libX11"; - - public const int None = 0; - public const int True = 1; - public const int False = 0; - - [DllImport(libX11)] - public extern static IntPtr XOpenDisplay(string display_name); - [DllImport(libX11)] - public extern static int XFree(IntPtr data); - [DllImport(libX11)] - public extern static int XDefaultScreen(IntPtr display); - [DllImport(libX11)] - public extern static IntPtr XRootWindow(IntPtr display, int screen); - [DllImport(libX11)] - public extern static IntPtr XCreatePixmap(IntPtr display, IntPtr d, uint width, uint height, uint depth); - [DllImport(libX11)] - public extern static IntPtr XFreePixmap(IntPtr display, IntPtr pixmap); - - [StructLayout(LayoutKind.Sequential)] - public struct XVisualInfo - { - public IntPtr visual; - public IntPtr visualid; - public int screen; - public int depth; - public XVisualClass c_class; - public ulong red_mask; - public ulong green_mask; - public ulong blue_mask; - public int colormap_size; - public int bits_per_rgb; - - public override string ToString() - { - return - "{XVisualInfo " + - $", visual={visual}" + - $", visualid={visualid}" + - $", screen={screen}" + - $", depth={depth}" + - $", c_class={c_class}" + - $", red_mask={red_mask}" + - $", green_mask={green_mask}" + - $", blue_mask={blue_mask}" + - $", colormap_size={colormap_size}" + - $", bits_per_rgb={bits_per_rgb}" + - "}"; - } - } - } - - internal enum XVisualClass : int { - StaticGray = 0, - GrayScale = 1, - StaticColor = 2, - PseudoColor = 3, - TrueColor = 4, - DirectColor = 5 - } - - internal class Glx - { - private const string libGL = "libGL"; - - public const int GLX_USE_GL = 1; - public const int GLX_BUFFER_SIZE = 2; - public const int GLX_LEVEL = 3; - public const int GLX_RGBA = 4; - public const int GLX_DOUBLEBUFFER = 5; - public const int GLX_STEREO = 6; - public const int GLX_AUX_BUFFERS = 7; - public const int GLX_RED_SIZE = 8; - public const int GLX_GREEN_SIZE = 9; - public const int GLX_BLUE_SIZE = 10; - public const int GLX_ALPHA_SIZE = 11; - public const int GLX_DEPTH_SIZE = 12; - public const int GLX_STENCIL_SIZE = 13; - public const int GLX_ACCUM_RED_SIZE = 14; - public const int GLX_ACCUM_GREEN_SIZE = 15; - public const int GLX_ACCUM_BLUE_SIZE = 16; - public const int GLX_ACCUM_ALPHA_SIZE = 17; - - public const int GLX_DRAWABLE_TYPE = 0x8010; - public const int GLX_RENDER_TYPE = 0x8011; - public const int GLX_X_RENDERABLE = 0x8012; - public const int GLX_RGBA_TYPE = 0x8014; - public const int GLX_COLOR_INDEX_TYPE = 0x8015; - - public const int GLX_PIXMAP_BIT = 0x00000002; - - public const int GLX_RGBA_BIT = 0x00000001; - - public const int GLX_SAMPLE_BUFFERS = 0x186a0; - public const int GLX_SAMPLES = 0x186a1; - - public const int GLX_CONTEXT_DEBUG_BIT_ARB = 0x00000001; - public const int GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x00000002; - public const int GLX_CONTEXT_MAJOR_VERSION_ARB = 0x2091; - public const int GLX_CONTEXT_MINOR_VERSION_ARB = 0x2092; - public const int GLX_CONTEXT_FLAGS_ARB = 0x2094; - - public const int GLX_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001; - public const int GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002; - public const int GLX_CONTEXT_PROFILE_MASK_ARB = 0x9126; - - static Glx() - { - var ptr = glXGetProcAddressARB("glXCreateContextAttribsARB"); - if (ptr != IntPtr.Zero) { - glXCreateContextAttribsARB = (glXCreateContextAttribsARBDelegate)Marshal.GetDelegateForFunctionPointer(ptr, typeof(glXCreateContextAttribsARBDelegate)); - } - } - - [DllImport(libGL)] - public extern static bool glXQueryVersion(IntPtr dpy, out int maj, out int min); - [DllImport(libGL)] - public extern static IntPtr glXChooseFBConfig(IntPtr dpy, int screen, [In] int[] attribList, out int nitems); - public static IntPtr[] ChooseFBConfig(IntPtr dpy, int screen, int[] attribList) - { - int nitems; - var fbcArrayPtr = glXChooseFBConfig(dpy, screen, attribList, out nitems); - - var fbcArray = new IntPtr[nitems]; - Marshal.Copy(fbcArrayPtr, fbcArray, 0, nitems); - - Xlib.XFree(fbcArrayPtr); - - return fbcArray; - } - [DllImport(libGL)] - public extern static IntPtr glXGetVisualFromFBConfig(IntPtr dpy, IntPtr config); - public static Xlib.XVisualInfo GetVisualFromFBConfig(IntPtr dpy, IntPtr config) - { - var visualPtr = glXGetVisualFromFBConfig(dpy, config); - if (visualPtr == IntPtr.Zero) { - throw new Exception("Failed to retrieve visual from framebuffer config."); - } - - var visual = (Xlib.XVisualInfo) Marshal.PtrToStructure(visualPtr, typeof(Xlib.XVisualInfo)); - - Xlib.XFree(visualPtr); - - return visual; - } - [DllImport(libGL)] - public extern static bool glXMakeCurrent(IntPtr dpy, IntPtr drawable, IntPtr ctx); - [DllImport(libGL)] - public extern static bool glXSwapBuffers(IntPtr dpy, IntPtr drawable); - [DllImport(libGL)] - public extern static bool glXIsDirect(IntPtr dpy, IntPtr ctx); - [DllImport(libGL)] - public extern static int glXGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); - [DllImport(libGL)] - public extern static IntPtr glXCreateGLXPixmap(IntPtr dpy, ref Xlib.XVisualInfo visual, IntPtr pixmap); - [DllImport(libGL)] - public extern static void glXDestroyGLXPixmap(IntPtr dpy, IntPtr pixmap); - [DllImport(libGL)] - public extern static void glXDestroyContext(IntPtr dpy, IntPtr ctx); - [DllImport(libGL)] - public extern static IntPtr glXQueryExtensionsString(IntPtr dpy, int screen); - public static string QueryExtensionsString(IntPtr dpy, int screen) - { - return Marshal.PtrToStringAnsi(glXQueryExtensionsString(dpy, screen)); - } - public static string[] QueryExtensions(IntPtr dpy, int screen) - { - var str = QueryExtensionsString(dpy, screen); - if (string.IsNullOrEmpty(str)) { - return new string[0]; - } - return str.Split(' '); - } - [DllImport(libGL)] - public extern static IntPtr glXGetProcAddressARB(string procname); - [DllImport(libGL)] - public extern static IntPtr glXCreateNewContext(IntPtr dpy, IntPtr config, int renderType, IntPtr shareList, int direct); - public static readonly glXCreateContextAttribsARBDelegate glXCreateContextAttribsARB; - public delegate IntPtr glXCreateContextAttribsARBDelegate(IntPtr dpy, IntPtr config, IntPtr share_context, int direct, int[] attrib_list); - } -} diff --git a/tests/Tests/SKTest.cs b/tests/Tests/SKTest.cs index 8e39f13a..129bf751 100644 --- a/tests/Tests/SKTest.cs +++ b/tests/Tests/SKTest.cs @@ -61,19 +61,10 @@ namespace SkiaSharp.Tests } else if (IsMac) { return new CglContext(); } else if (IsWindows) { - return null; + return new WglContext(); } else { return null; } } } - - public abstract class GlContext : IDisposable - { - public abstract void MakeCurrent(); - public abstract void SwapBuffers(); - public abstract void Destroy(); - - void IDisposable.Dispose() => Destroy(); - } }