使用 P/Invoke 从 C# 编组 "EGLRenderResolutionScaleProperty" 到 ANGLE
Marshalling "EGLRenderResolutionScaleProperty" to ANGLE from C# using P/Invoke
我正在尝试使用 P/Invoke 让 ANGLE 在 C# 中工作。基本上,我正在创建一个简单的 2D 表面,然后将其传递给 skia(使用 SkiaSharp)。一切正常,但我在编组 PropertySet
到非托管代码时遇到问题。
这个位工作正常:
// 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);
我的导入是这样的:
[DllImport("libEGL.dll")]
public static extern IntPtr eglCreateWindowSurface(
IntPtr dpy,
IntPtr config,
[MarshalAs(UnmanagedType.IInspectable)] object win,
int[] attrib_list);
当我尝试为高分辨率屏幕设置缩放比例时出现问题。 "should" 作品:
var scale = PropertyValue.CreateSingle(2);
props.Add("EGLRenderResolutionScaleProperty", scale);
它在使用 C++ 时有效,但在 C# 中无效。我想我已经把它归结为实际值没有被正确编组的事实。这是因为用ANGLE代码调试的时候死在这里:
ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue;
ABI::Windows::Foundation::PropertyType propertyType;
// ...
result = propertyValue->get_Type(&propertyType);
运行时异常是:
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.
是否有任何提示或解决方案?
以下是我的全部代码供参考:https://gist.github.com/mattleibow/eacb9c9e87f306b218d99c713c532a82
原始角度问题:https://github.com/Microsoft/angle/issues/89
编辑
经过进一步调查,我发现由于某种原因,C# 中的 PropertyValue
与 C++ 中的同一类型不同(通过 Windows 运行时组件)。
我试过这个:
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;
}
此方法在 C++ 中创建了一个 PropertySet
,然后将其 return 发送到 C#。这很好用,我的 ANGLE 会话也很好。
现在,如果我直接将代码更改为 return 和 PropertyValue::CreateSingle(scale)
,这将再次失败。如果我将 C# 中的 PropertyValue.CreateSingle(scale)
传递给此 C++ 方法,我发现对象不相似。
在调试器局部变量中,我为在 C# 中构造的对象得到了这个:
0x09f10ba4 <Information not available, no symbols loaded for coreclr.dll>
Platform::Object ^
如果对象是用 C++ 构造的,我得到:
0x019162e8 2.00000000
Platform::Object ^ {WinTypes.dll!Windows::Foundation::Value<Windows::Foundation::ValueScalar<float> >}
对象不应该相同吗,因为它们是同一类型?更可怕的是,如果我越界传这个值,类型就变了。
删除了编辑 2 并将其设置为答案。
我不是直接回答你的问题。
但是一种行之有效的替代方法是创建一个 C++ WindowsRuntime 组件包装器项目,也就是使用 C++/CLI(或者我应该说是 CXX?)来进行互操作。
您可以像下面这样公开方法,根据您的需要公开 API。
void InitializeEGL(Windows::UI::Core::CoreWindow^ window);
void InitializeEGL(Windows::UI::Xaml::Controls::SwapChainPanel ^ window);
或者,如果您想要更精细的粒度,请在 C++ WinRT 组件中声明如下内容,
void EglCreateWindowSurface(Platform::IntPtr display, Platform::IntPtr config, Windows::Foundation::Collections::PropertySet^ propertySet);
这个小技巧将使您能够绕过 C# 封送处理问题,并且
我已经验证它可以工作。
我将继续研究 C# 互操作方法。
经过一些搜索,我发现最好(也是唯一)的方法是创建一个 Windows 运行时组件。
因为我无法获得对该组件的 "hard" 引用 - 该库必须在没有 ANGLE 的情况下工作。我选择了一个带有 C API 的小组件,这样我就可以在需要时 P/Invoke 它。这是我在 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);
}
我在这里使用 COM,以便我可以在 C/C++ 代码中使用它。我的 P/Invoke 代码是这样的:
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);
}
我正在尝试使用 P/Invoke 让 ANGLE 在 C# 中工作。基本上,我正在创建一个简单的 2D 表面,然后将其传递给 skia(使用 SkiaSharp)。一切正常,但我在编组 PropertySet
到非托管代码时遇到问题。
这个位工作正常:
// 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);
我的导入是这样的:
[DllImport("libEGL.dll")]
public static extern IntPtr eglCreateWindowSurface(
IntPtr dpy,
IntPtr config,
[MarshalAs(UnmanagedType.IInspectable)] object win,
int[] attrib_list);
当我尝试为高分辨率屏幕设置缩放比例时出现问题。 "should" 作品:
var scale = PropertyValue.CreateSingle(2);
props.Add("EGLRenderResolutionScaleProperty", scale);
它在使用 C++ 时有效,但在 C# 中无效。我想我已经把它归结为实际值没有被正确编组的事实。这是因为用ANGLE代码调试的时候死在这里:
ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue;
ABI::Windows::Foundation::PropertyType propertyType;
// ...
result = propertyValue->get_Type(&propertyType);
运行时异常是:
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.
是否有任何提示或解决方案?
以下是我的全部代码供参考:https://gist.github.com/mattleibow/eacb9c9e87f306b218d99c713c532a82
原始角度问题:https://github.com/Microsoft/angle/issues/89
编辑
经过进一步调查,我发现由于某种原因,C# 中的 PropertyValue
与 C++ 中的同一类型不同(通过 Windows 运行时组件)。
我试过这个:
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;
}
此方法在 C++ 中创建了一个 PropertySet
,然后将其 return 发送到 C#。这很好用,我的 ANGLE 会话也很好。
现在,如果我直接将代码更改为 return 和 PropertyValue::CreateSingle(scale)
,这将再次失败。如果我将 C# 中的 PropertyValue.CreateSingle(scale)
传递给此 C++ 方法,我发现对象不相似。
在调试器局部变量中,我为在 C# 中构造的对象得到了这个:
0x09f10ba4 <Information not available, no symbols loaded for coreclr.dll>
Platform::Object ^
如果对象是用 C++ 构造的,我得到:
0x019162e8 2.00000000
Platform::Object ^ {WinTypes.dll!Windows::Foundation::Value<Windows::Foundation::ValueScalar<float> >}
对象不应该相同吗,因为它们是同一类型?更可怕的是,如果我越界传这个值,类型就变了。
删除了编辑 2 并将其设置为答案。
我不是直接回答你的问题。
但是一种行之有效的替代方法是创建一个 C++ WindowsRuntime 组件包装器项目,也就是使用 C++/CLI(或者我应该说是 CXX?)来进行互操作。
您可以像下面这样公开方法,根据您的需要公开 API。
void InitializeEGL(Windows::UI::Core::CoreWindow^ window);
void InitializeEGL(Windows::UI::Xaml::Controls::SwapChainPanel ^ window);
或者,如果您想要更精细的粒度,请在 C++ WinRT 组件中声明如下内容,
void EglCreateWindowSurface(Platform::IntPtr display, Platform::IntPtr config, Windows::Foundation::Collections::PropertySet^ propertySet);
这个小技巧将使您能够绕过 C# 封送处理问题,并且 我已经验证它可以工作。
我将继续研究 C# 互操作方法。
经过一些搜索,我发现最好(也是唯一)的方法是创建一个 Windows 运行时组件。
因为我无法获得对该组件的 "hard" 引用 - 该库必须在没有 ANGLE 的情况下工作。我选择了一个带有 C API 的小组件,这样我就可以在需要时 P/Invoke 它。这是我在 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);
}
我在这里使用 COM,以便我可以在 C/C++ 代码中使用它。我的 P/Invoke 代码是这样的:
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);
}