问题
I am trying to get ANGLE working in C# using P/Invoke. Basically, I am creating a simple 2D surface, and then passing that off to skia (using SkiaSharp). Everything is working and all that, but I am having a problem marshalling PropertySet
to the unmanaged code.
This bit works fine:
// the properties
var props = new PropertySet();
props.Add("EGLNativeWindowTypeProperty", swapChainPanel);
// the surface attributes
int[] surfaceAttrs = {
EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER, EGL_TRUE,
EGL_NONE
};
// create the surface
surface = eglCreateWindowSurface(eglDisplay, eglConfig, props, surfaceAttrs);
My import looks like this:
[DllImport("libEGL.dll")]
public static extern IntPtr eglCreateWindowSurface(
IntPtr dpy,
IntPtr config,
[MarshalAs(UnmanagedType.IInspectable)] object win,
int[] attrib_list);
The problem comes in when I am trying to set my scaling for high resolution screens. This "should" work:
var scale = PropertyValue.CreateSingle(2);
props.Add("EGLRenderResolutionScaleProperty", scale);
It works when using C++, but not in C#. I think I have got it down to the fact that the actual value is not being marshalled correctly. This is because when debugging in ANGLE code, it dies over here:
ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue;
ABI::Windows::Foundation::PropertyType propertyType;
// ...
result = propertyValue->get_Type(&propertyType);
https://github.com/Microsoft/angle/blob/54b1fd01f7fddcd7011d5a04e9259edace8a13da/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp#L242
The runtime exception is:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
Are there any tips, or solutions?
Here is all my code for reference: https://gist.github.com/mattleibow/eacb9c9e87f306b218d99c713c532a82
Original ANGLE issue: https://github.com/Microsoft/angle/issues/89
EDIT
After further investigation, I found that for some reason PropertyValue
in C# is different to the same type in C++ (via Windows Runtime Component).
I tried this:
static PropertySet^ CreateSurface(SwapChainPanel^ panel, float scale)
{
PropertySet^ surfaceCreationProperties = ref new PropertySet();
surfaceCreationProperties->Insert(L"EGLNativeWindowTypeProperty", panel);
Object^ scaleVal = PropertyValue::CreateSingle(scale);
surfaceCreationProperties->Insert(L"EGLRenderResolutionScaleProperty", scaleVal);
return surfaceCreationProperties;
}
This method created a PropertySet
in C++ and then returns that to C#. This works fine and my ANGLE session is all good.
Now, if I change the code to return the PropertyValue::CreateSingle(scale)
directly, this fails again. If I pass the PropertyValue.CreateSingle(scale)
from C# into this C++ method, I find that the objects are not similar.
In the debugger locals I get this for the object constructed in C#:
0x09f10ba4 <Information not available, no symbols loaded for coreclr.dll>
Platform::Object ^
If the object is constructed in C++, I get this:
0x019162e8 2.00000000
Platform::Object ^ {WinTypes.dll!Windows::Foundation::Value<Windows::Foundation::ValueScalar<float> >}
Shouldn't the object be the same, since they are the same type? What is more scary is that if I pass this value across the boundary, the type changes.
Removed edit 2 and set it as an answer.
回答1:
I'm not answering your question directly.
But a proven-working alternative way is to create a C++ WindowsRuntime Component wrapper project, aka, use C++/CLI(or CXX, should I say?)to do the interop.
You can expose methods like below, expose APIs depending on your needs.
void InitializeEGL(Windows::UI::Core::CoreWindow^ window);
void InitializeEGL(Windows::UI::Xaml::Controls::SwapChainPanel ^ window);
or, if you want to have finer granularity, declare something like below in the C++ WinRT component,
void EglCreateWindowSurface(Platform::IntPtr display, Platform::IntPtr config, Windows::Foundation::Collections::PropertySet^ propertySet);
This little trick will enable you to get around the C# marshaling issue, and I have verfied it to work.
And I'll continue to investigate the C# interop approach.
回答2:
After doing a bit of searching, I found the best (and only) way to do this was to create a Windows Runtime Component.
As I could not have a "hard" reference to the component - this library must work without ANGLE present. I opted for a small component with a C API so that I could just P/Invoke it when I needed it. This was my method in C++:
void PropertySetInterop_AddSingle(ABI::Windows::Foundation::Collections::IPropertySet* propertySet, HSTRING key, float scale)
{
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
ComPtr<IPropertySet> propSet = propertySet;
ComPtr<IMap<HSTRING, IInspectable*>> map;
propSet.As(&map);
ComPtr<IPropertyValueStatics> propValueFactory;
GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propValueFactory);
ComPtr<IInspectable> valueInspectable;
propValueFactory->CreateSingle(scale, &valueInspectable);
boolean replaced;
map->Insert(key, valueInspectable.Get(), &replaced);
}
I am using COM here so that I can use this in C/C++ code. My P/Invoke code is this:
internal static class PropertySetInterop
{
public static void AddSingle(PropertySet properties, string key, float value)
{
PropertySetInterop_AddSingle(properties, key, value);
}
public static void AddSize(PropertySet properties, string key, float width, float height)
{
PropertySetInterop_AddSize(properties, key, width, height);
}
private const string libInterop = "SkiaSharp.Views.Interop.UWP.dll";
[DllImport(libInterop)]
private static extern void PropertySetInterop_AddSingle(
[MarshalAs(UnmanagedType.IInspectable)] object properties,
[MarshalAs(UnmanagedType.HString)] string key,
float value);
[DllImport(libInterop)]
private static extern void PropertySetInterop_AddSize(
[MarshalAs(UnmanagedType.IInspectable)] object properties,
[MarshalAs(UnmanagedType.HString)] string key,
float width, float height);
}
来源:https://stackoverflow.com/questions/39432377/marshalling-eglrenderresolutionscaleproperty-to-angle-from-c-sharp-using-p-inv