【问题标题】:Marshalling "EGLRenderResolutionScaleProperty" to ANGLE from C# using P/Invoke使用 P/Invoke 将“EGLRenderResolutionScaleProperty”从 C# 编组为 ANGLE
【发布时间】:2017-01-18 19:58:51
【问题描述】:

我正在尝试使用 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);

当我尝试为高分辨率屏幕设置缩放比例时,问题就出现了。这个“应该”工作:

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);

https://github.com/Microsoft/angle/blob/54b1fd01f7fddcd7011d5a04e9259edace8a13da/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp#L242

运行时异常是:

运行时检查失败 #0 - ESP 的值未在函数调用中正确保存。这通常是用一个调用约定声明的函数和一个用不同调用约定声明的函数指针调用的结果。

有什么提示或解决方案吗?

这是我所有的代码供参考: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,然后将其返回给 C#。这很好用,我的 ANGLE 会话也很好。

现在,如果我将代码更改为直接返回 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# 程序中使用 PropertySet,因为它是特定于 C++ 的语言投影助手。 C# 投影是字典。在this post 中有更多关于此的信息,包括“不要这样做!”解释。也很难猜出你为什么会使用skia,它的角度工作正常,然后你会得到DirectX调用。所以你不妨跳过所有可怕的胶水。
  • 感谢您的回复,但我在编组 PropertySet/PropertyValue 时遇到问题。经过进一步调查,同一类型的托管 C++ 和 C# 版本之间似乎存在差异。我创建了一个运行时组件,但仍然失败 - 请参阅我的编辑
  • 我看到有一个关闭请求:(这与另一个问题不太一样。这与编组PropertyValue有关,而不是直接角度了。其他一切似乎都在工作很好。

标签: c# windows-runtime uwp marshalling angle


【解决方案1】:

我没有直接回答你的问题。

但一种行之有效的替代方法是创建一个 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# 互操作方法。

【讨论】:

  • 谢谢。我最终广告制作了一个 WinRT 组件。我创建了一个 C API,所以我可以 P/Invoke(我需要)。我尝试直接使用 COM,但遇到了一个问题,因为 IPropertyValue 已存在于托管区域中。但是,这并不相同。在对来自PropertyValue::CreateSingle 的结果进行编组时,会将其转换为float,从而丢失了实际身份和几个成员,例如ANGLE 需要的get_Type 方法。
  • 基本上,在 C++ 中,CreateSingle 的结果是 ValueScalar&lt;float&gt;,但在 C# 中,出于某种原因,它只是一个普通的 float
  • 我将此标记为已回答,因为这是我必须做的。如果有更好的方法,那么我们总是可以回来。谢谢。
【解决方案2】:

经过一番搜索,我发现最好的(也是唯一的)方法是创建一个 Windows 运行时组件。

因为我无法对组件进行“硬”引用 - 这个库必须在没有 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);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多