简体   繁体   中英

Attempting to convert C++ into C# with interop

I've got a program that calls to EGL in C++. I want to make the same call in C#, but there doesn't seem to be an equivalent concept in C#.

I'm getting a read/write access denied error when the execution context enters the C++ EGL code.

This is the code in the C++ program that I'm trying to convert to C#:

PropertySet^ surfaceCreationProperties = ref new PropertySet();
surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), somethingOtherThanAWindow);

mEglSurface = eglCreateWindowSurface(mEglDisplay, config, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes));

I have a C# class which converts C# EGL calls into C++ calls. I believe the C++ is unmanaged, though I wouldn't know how to tell you for certain.

The C# class looks like this:

public static IntPtr CreateWindowSurface(IntPtr dpy, IntPtr config, IntPtr win, int[] attrib_list)
{
    IntPtr retValue;

    unsafe {
        fixed (int* p_attrib_list = attrib_list)
        {
            retValue = Delegates.peglCreateWindowSurface(dpy, config, win, p_attrib_list);
        }
    }
    return (retValue);
}

More of that code can be seen here: https://github.com/luca-piccioni/OpenGL.Net/blob/master/OpenGL.Net/Egl.VERSION_1_0.cs#L751

You may notice that this method has an IntPtr win -- this is where I'm passing the PropertySet . Typically I believe this would be a System.Windows.Forms.Control , but some checking is done in the C++ EGL code to see if it is, or if it's a PropertySet .

The C++ method that is being called is this:

EGLSurface EGLAPIENTRY CreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list)

More can be seen here: https://github.com/Microsoft/angle/blob/ms-holographic-experimental/src/libGLESv2/entry_points_egl.cpp#L241

As you can see, the C++ method is expecting an EGLNativeWindowType. I'm not exactly sure what the relationship to that is between an IInspectable, and a PropertSet - it seems strange that this can be casted.

EGLNativeWindowType has the following type definition:

typedef HWND EGLNativeWindowType;

Which implies it has to be some sort of window handle. I don't understand how a PropertySet could be a window handle.

I suspect the main problem is around choosing the correct type of object to pass to the C# EGL implementation. PropertySet seems like it might be the right choice, but the reinterpret_cast is really throwing me off.

Can anyone walk me through this?

Typically I believe this would be a System.Windows.Forms.Control...

It is a painfully wrong assumption. Making sense of the typing requires writing three books, pretty hard to do in an SO answer. If you actually intend to do this from a Winforms app then stop right now, that can never work.

OpenGL uses very loose typing, the arguments to their api functions are nothing much more than void* , a raw pointer. Which makes it very flexible but it is really important what the pointer actually points to. If the client program and the video adapter interface don't agree about that in the slightest way then your program will build just fine but will crash and burn in completely undiagnosable way at runtime. A major reason why Microsoft abandoned OpenGL and decided to create their own, DirectX was the result.

Which uses pointers as well but they are the smarter kind, they support type discovery at runtime. They are IUnknown pointers, its QueryInterface() method permits finding out if an object supports a specific expected interface. The flavor you see being used here is the exact same kind of pointer, IInspectable is a slightly smarter version than IUnknown and the base interface implemented by all WinRT classes. You really do have to pass an IInspectable* since that is what the ANGLE port expects.

You'd generally expect that you could just pass an ICoreWindow interface pointer and be done with it, that's the WinRT interface for a window. The renderer however requires more information than just the ICoreWindow. Not exactly sure why, I think it has something to do with resolution Independence in WinRT. It also needs to know the surface size and scaling factor.

Problem is, OpenGL doesn't have a way to pass that info. So the Microsoft programmer used a very hokey hack, instead of adding a function to pass this info he ab/used the ability to pass any kind of IInspectable*, he passes a IMap<String^, IInspectable*> pointer. Basically a property bag, CoreWindowNativeWindow.cpp in the ANGLE port digs the properties out of the bag again in its CoreWindowNativeWindow::initialize() function.

PropertySet is a concrete class in the C++ language projection that implements IMap<K, V> . Do note it is specific to C++, in C# you'd use a Dictionary<string, IntPtr> instead. The language projection built into the CLR automatically maps a managed dictionary to the native IMap interface.

Oh joy, more IntPtrs. IInspectable* is completely hidden in the language projection you use in C#, that doesn't make it easy. I'm 98% sure that you can use Marshal.GetIUnknownForObject() to obtain a pointer that works, even though it is wrong flavor. Since the C++ code does the right thing and uses QueryInterface :) You must call Marshal.Release() afterwards to clean up, not doing so causes a memory leak.

Do note that there are strong overtones of you doing the wrong thing. I think you are, Microsoft provided this ANGLE fork only for one reason. They tried to make it easy for companies to port their iOS game to WinRT/UWP. Kinda necessary to fill the store with the kind of games that customers like. The ANGLE port was only ever intended to be easy to use from code that started in ObjectiveC or C++, the kind of languages used to write these games.

They could have made it a lot easier to use the library from languages like Javascript or C#, they didn't because it wasn't necessary. If you have to translate a metric mile of C++ code that uses OpenGL to C# then it is pretty likely you'd be much better off when you use DirectX instead. Expect more of this type mapping trouble with other functions and be weary of an experimental HoloLens port.

I think that the parameter type is definitely wrong.

For a complete example, you should read the DeviceContext implementation in https://github.com/luca-piccioni/OpenGL.Net/blob/master/OpenGL.Net/NativeDeviceContext.cs . You should also see where this code is called, so you get the actual calls sequence needed to initialize EGL: - factory method: https://github.com/luca-piccioni/OpenGL.Net/blob/master/OpenGL.Net/DeviceContextFactory.cs - control integration: https://github.com/luca-piccioni/OpenGL.Net/blob/master/OpenGL.Net/GlControl.cs

As you can see, the handle is the Control.Handle property. Probably the actual value to pass is dependent on the current OS implementing EGL, but it should be an handle of the window (or control) hosting the drawing results.


Alternatively, you can check the actual EGL method implementation, and follow the parameter usage untill to the actual DirectX call, just I did at that time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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