简体   繁体   English

使用 P/Invoke 从 C# 将“EGLRenderResolutionScaleProperty”编组到 ANGLE

[英]Marshalling "EGLRenderResolutionScaleProperty" to ANGLE from C# using P/Invoke

I am trying to get ANGLE working in C# using P/Invoke.我正在尝试使用 P/Invoke 使 ANGLE 在 C# 中工作。 Basically, I am creating a simple 2D surface, and then passing that off to skia (using SkiaSharp).基本上,我正在创建一个简单的 2D 表面,然后将其传递给 Skia(使用 SkiaSharp)。 Everything is working and all that, but I am having a problem marshalling PropertySet to the unmanaged code.一切正常,但我在将PropertySet编组到非托管代码时遇到问题。

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#.它在使用 C++ 时有效,但在 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:这是因为在 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 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.运行时检查失败 #0 - ESP 的值未在函数调用中正确保存。 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这是我所有的代码供参考: https : //gist.github.com/mattleibow/eacb9c9e87f306b218d99c713c532a82

Original ANGLE issue: https://github.com/Microsoft/angle/issues/89原始角度问题: 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).经过进一步调查,我发现由于某种原因,C# 中的PropertyValue与 C++ 中的相同类型不同(通过 Windows 运行时组件)。

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#.此方法在 C++ 中创建了一个PropertySet ,然后将其返回给 C#。 This works fine and my ANGLE session is all good.这很好用,我的 ANGLE 会话也很好。

Now, if I change the code to return the PropertyValue::CreateSingle(scale) directly, this fails again.现在,如果我更改代码以直接返回PropertyValue::CreateSingle(scale) ,这将再次失败。 If I pass the PropertyValue.CreateSingle(scale) from C# into this C++ method, I find that the objects are not similar.如果我将 C# 中的PropertyValue.CreateSingle(scale)传递到这个 C++ 方法中,我发现这些对象并不相似。

In the debugger locals I get this for the object constructed in C#:在调试器本地人中,我为在 C# 中构造的对象得到了这个:

0x09f10ba4 <Information not available, no symbols loaded for coreclr.dll>
Platform::Object ^

If the object is constructed in C++, I get this:如果对象是用 C++ 构造的,我会得到这个:

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.删除了编辑 2 并将其设置为答案。

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.但一个行之有效的替代方法是创建一个 C++ WindowsRuntime 组件包装器项目,也就是使用 C++/CLI(或 CXX,我应该说?)来执行互操作。

You can expose methods like below, expose APIs depending on your needs.您可以公开如下方法,根据您的需要公开 API。

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,或者,如果您想要更精细的粒度,请在 C++ WinRT 组件中声明如下内容,

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.这个小技巧将使您能够解决 C# 封送处理问题,我已经验证它可以工作。

And I'll continue to investigate the C# interop approach.我将继续研究 C# 互操作方法。

After doing a bit of searching, I found the best (and only) way to do this was to create a Windows Runtime Component.在做了一些搜索之后,我发现最好的(也是唯一的)方法是创建一个 Windows 运行时组件。

As I could not have a "hard" reference to the component - this library must work without ANGLE present.由于我无法对组件进行“硬”引用 - 该库必须在没有 ANGLE 的情况下工作。 I opted for a small component with a C API so that I could just P/Invoke it when I needed it.我选择了一个带有 C API 的小组件,这样我就可以在需要时直接调用它。 This was my method in C++:这是我在 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.我在这里使用 COM,以便我可以在 C/C++ 代码中使用它。 My P/Invoke code is this:我的 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM