Uno.SkiaSharp/tests/Tests/GRGlInterfaceTest.cs

86 строки
2.2 KiB
C#
Исходник Обычный вид История

2017-02-14 15:43:26 +03:00
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Xunit;
2017-02-14 15:43:26 +03:00
namespace SkiaSharp.Tests
{
2017-02-14 23:32:10 +03:00
public class GRGlInterfaceTest : SKTest
2017-02-14 15:43:26 +03:00
{
[SkippableFact]
public void InterfaceConstructionWithoutContextDoesNotCrash()
{
var glInterface = GRGlInterface.Create();
Assert.Null(glInterface);
}
Re-work the managed-native types (#900) Changes: - Added `GCHandleProxy` to debug builds - this is used to track all `GCHandle` `Alloc` and `Free` calls to ensure that all allocations are freed. - added some unit tests to make sure this is actually enforced - as a result, several object are now freed correctly - Added `ISKReferenceCounted` and `ISKNonVirtualReferenceCounted` interfaces to represent the reference counting types used in the native library - this helps with automatically de-referencing objects - `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` have been re-written to use better delegates - instead of passing each of the delegates as parameters, they are now a struct that is passed as a single object - better for extensions (which there shouldn't be) and only a single static field on the type - removed the usage of `Marshal.GetFunctionPointerForDelegate`, which should help out with WASM (see #876) - the objects now only keep weak references, meaning that they can now be garbage collected - instead of trying to resolve the instances with a dictionary, a delegate is used and passed as "user context" - Moved some of the repetitive logic from the types into the base `SKObject` and `SKNativeObject` - some logic is automatically executed if the concrete type is `ISKReferenceCounted` or `ISKNonVirtualReferenceCounted` - with the more centralized logic and stricter patterns, better tests can be written to make sure all memory is freed correctly and timely - `SKData`, `SKFontManager` and `SKTypeface` now correctly prevent disposal of the "static" instances - `SKPaint` now references the `Shader`, `MaskFilter`, `ColorFilter`, `ImageFilter`, `Typeface` and `PathEffect` properties - this prevents accidental collection, or non-collection when the object goes out of scope - the `SKPath` iterators (`Iterator` and `RawIterator`) and op builder (`OpBuilder`) now correctly own and dispose their native objects - `SKRegion` objects are now disposed on the native side - `SKTypeface` construction from a `SKManagedStream` (via both `SKTypeface` and `SKFontManager`) now copy the contents of the .NET `Stream` into a native memory - typeface construction requires multiple seeks (previously, the stream was copied only if it was non-seekable) - it also requires "duplicating" the stream, which is not supported on .NET streams - duplicates or forks of a stream means that each of the streams need to be read concurrently from different locations - .NET streams can only have a single position - Updated the NuGets used for the tests - using the `Xunit.AssemblyFixture` and `Xunit.SkippableFact` NuGets instead of using the code directly - removed the `Xunit.Categories` NuGet as it was preventing tests from running This PR has a big set of changes that may be breaking due to bug fixes: - The `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` no longer prevent the GC from collecting them. This means that if code no longer references them, they will be disposed. - As far as I can tell, this should not be a problem for the streams as they are never kept around - they are just used for reading and writing and typically only need to live for as long as a single method, and then need to be disposed by the caller. The `SKTypeface` and `SKDocument` do keep it around for a bit, but then they also take ownership of the stream and keep a hard reference to the streams themselves. They will dispose the streams when they are disposed. - `SKDrawable` is never kept around and is entirely a user-controlled object. If it goes out of scope, skia doesn't have a reference anyway. - The `SKFontManager` and `SKTypeface` no longer use the managed streams (`SKManagedStream` or `Stream`) directly - they make a copy. - This is simply because skia streams can do things that are not possible for .NET - they can be read concurrently from different positions. If a `SKFileStream` or `SKMemoryStream` are passed, then the streams are not copied. - Further optimizations can be made in the case of a `MemoryStream` or `byte[]` to not actually copy but use GC pinning to get a handle to the managed data and work with pointers. But this can be done later so that this PR can be merged and tested.
2019-07-30 04:26:21 +03:00
[Trait(CategoryKey, GpuCategory)]
[SkippableFact]
2017-02-14 15:43:26 +03:00
public void CreateDefaultInterfaceIsValid()
{
using (var ctx = CreateGlContext()) {
ctx.MakeCurrent();
var glInterface = GRGlInterface.Create();
2017-02-14 15:43:26 +03:00
Assert.NotNull(glInterface);
Assert.True(glInterface.Validate());
}
}
Re-work the managed-native types (#900) Changes: - Added `GCHandleProxy` to debug builds - this is used to track all `GCHandle` `Alloc` and `Free` calls to ensure that all allocations are freed. - added some unit tests to make sure this is actually enforced - as a result, several object are now freed correctly - Added `ISKReferenceCounted` and `ISKNonVirtualReferenceCounted` interfaces to represent the reference counting types used in the native library - this helps with automatically de-referencing objects - `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` have been re-written to use better delegates - instead of passing each of the delegates as parameters, they are now a struct that is passed as a single object - better for extensions (which there shouldn't be) and only a single static field on the type - removed the usage of `Marshal.GetFunctionPointerForDelegate`, which should help out with WASM (see #876) - the objects now only keep weak references, meaning that they can now be garbage collected - instead of trying to resolve the instances with a dictionary, a delegate is used and passed as "user context" - Moved some of the repetitive logic from the types into the base `SKObject` and `SKNativeObject` - some logic is automatically executed if the concrete type is `ISKReferenceCounted` or `ISKNonVirtualReferenceCounted` - with the more centralized logic and stricter patterns, better tests can be written to make sure all memory is freed correctly and timely - `SKData`, `SKFontManager` and `SKTypeface` now correctly prevent disposal of the "static" instances - `SKPaint` now references the `Shader`, `MaskFilter`, `ColorFilter`, `ImageFilter`, `Typeface` and `PathEffect` properties - this prevents accidental collection, or non-collection when the object goes out of scope - the `SKPath` iterators (`Iterator` and `RawIterator`) and op builder (`OpBuilder`) now correctly own and dispose their native objects - `SKRegion` objects are now disposed on the native side - `SKTypeface` construction from a `SKManagedStream` (via both `SKTypeface` and `SKFontManager`) now copy the contents of the .NET `Stream` into a native memory - typeface construction requires multiple seeks (previously, the stream was copied only if it was non-seekable) - it also requires "duplicating" the stream, which is not supported on .NET streams - duplicates or forks of a stream means that each of the streams need to be read concurrently from different locations - .NET streams can only have a single position - Updated the NuGets used for the tests - using the `Xunit.AssemblyFixture` and `Xunit.SkippableFact` NuGets instead of using the code directly - removed the `Xunit.Categories` NuGet as it was preventing tests from running This PR has a big set of changes that may be breaking due to bug fixes: - The `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` no longer prevent the GC from collecting them. This means that if code no longer references them, they will be disposed. - As far as I can tell, this should not be a problem for the streams as they are never kept around - they are just used for reading and writing and typically only need to live for as long as a single method, and then need to be disposed by the caller. The `SKTypeface` and `SKDocument` do keep it around for a bit, but then they also take ownership of the stream and keep a hard reference to the streams themselves. They will dispose the streams when they are disposed. - `SKDrawable` is never kept around and is entirely a user-controlled object. If it goes out of scope, skia doesn't have a reference anyway. - The `SKFontManager` and `SKTypeface` no longer use the managed streams (`SKManagedStream` or `Stream`) directly - they make a copy. - This is simply because skia streams can do things that are not possible for .NET - they can be read concurrently from different positions. If a `SKFileStream` or `SKMemoryStream` are passed, then the streams are not copied. - Further optimizations can be made in the case of a `MemoryStream` or `byte[]` to not actually copy but use GC pinning to get a handle to the managed data and work with pointers. But this can be done later so that this PR can be merged and tested.
2019-07-30 04:26:21 +03:00
[Trait(CategoryKey, GpuCategory)]
[SkippableFact]
public void AssembleInterfaceIsValid()
{
using (var ctx = CreateGlContext()) {
ctx.MakeCurrent();
if (IsMac) {
var lib = LibraryLoader.LoadLibrary("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib");
var glInterface = GRGlInterface.Create(name => {
return LibraryLoader.GetSymbol(lib, name);
});
Assert.NotNull(glInterface);
Assert.True(glInterface.Validate());
LibraryLoader.FreeLibrary(lib);
} else if (IsWindows) {
var lib = LibraryLoader.LoadLibrary("opengl32.dll");
var glInterface = GRGlInterface.Create(name => {
var ptr = LibraryLoader.GetSymbol(lib, name);
if (ptr == IntPtr.Zero) {
ptr = wglGetProcAddress(name);
}
return ptr;
});
Assert.NotNull(glInterface);
Assert.True(glInterface.Validate());
LibraryLoader.FreeLibrary(lib);
} else if (IsLinux) {
var glInterface = GRGlInterface.Create(name => {
2017-02-17 04:59:07 +03:00
return glXGetProcAddress(name);
});
Assert.NotNull(glInterface);
Assert.True(glInterface.Validate());
} else {
// more platforms !!!
2017-02-17 04:51:26 +03:00
throw new Exception("Some strange platform that is not Windows, macOS nor Linux...");
}
}
}
[DllImport("opengl32.dll", CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr wglGetProcAddress([MarshalAs(UnmanagedType.LPStr)] string lpszProc);
2017-02-17 04:59:07 +03:00
[DllImport("libGL.so.1")]
public static extern IntPtr glXGetProcAddress([MarshalAs(UnmanagedType.LPStr)] string lpszProc);
2017-02-14 15:43:26 +03:00
}
}