0

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?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
iLoch
  • 741
  • 3
  • 11
  • 32
  • All I can say is that your code look very suspicious... I don't think that you could use `reinterpret_cast` on a managed handled to convert that to a native interface pointer. – Phil1970 Aug 18 '16 at 17:43
  • 1
    Also, given that EGL seems to be native library, you should probably do all native/managed conversion in C++/CLI (mixed mode) and only use managed code in C#. – Phil1970 Aug 18 '16 at 17:48
  • @Phil1970 That C++ - the reinterpret_cast stuff - works fine. If it would require that EGL be managed in order for that to work then that very well may be the case. (I'm using ANGLE, which is an OpenGL to DX11 library, which in my mind means there's a good chance it's managed.) – iLoch Aug 19 '16 at 20:33
  • @Phil1970 My main problem is with the memory access exception. I assumed it was because of some managed -> native process, but I may be wrong. – iLoch Aug 19 '16 at 20:34
  • 1
    It is very suspicious because I cannot find anywhere example of doing a `reinterpret_cast` from a manager reference to a native interface. Are you sure that you don't miss a call to `pin_ptr`? As you should know managed references are not the same as pointers and can be moved in memory so to me it look very dangerous to do such cast as if the garbage collector decide to run at that moment the native pointer might become invalid and cause your application to crash. – Phil1970 Aug 20 '16 at 14:31
  • If you do any serious mixed mode code, reading the book **C++/CLI In Action** by **Nishant Sivakumar** might be a good idea. – Phil1970 Aug 20 '16 at 14:34
  • @Phil1970 The reason for the reinterpret_cast and subsequent usage of the PropertySet is beyond my knowledge. The C++ code at the top of my question is written by some guys at Microsoft as part of an application template. Won't the GC only remove the PropertySet once it has determined it won't be used anymore? Isn't that the purpose of the caret? I suppose it's probably safe to do since the DLL only uses the object once. – iLoch Aug 20 '16 at 19:37
  • Well, it look that it is Windows Store application (C++/CX) and not desktop application (C++/CLI). See [Casting (C++/CX)](https://msdn.microsoft.com/en-us/library/hh755802.aspx) – Phil1970 Aug 21 '16 at 21:15

2 Answers2

4

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.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • This is a fantastic response, thank you. The real reason I'm doing all of this is so that I can translate WebGL calls from a JS interface into DirectX calls. Realistically if I knew DirectX well enough I'd just be writing the WebGL interface in DirectX, but I'm not. There is at least one demonstration of using OpenGL as a backend to WebGL on the web, so I'll be using that as a reference. – iLoch Aug 26 '16 at 01:26
  • I'm getting a `DXGI_SWAP_CHAIN_DESC.OutputWindow is not a valid window handle.` error using `IntPtr surfaceCreationPropertiesPointer = Marshal.GetIUnknownForObject(surfaceCreationProperties);` -- still I think your answer is the best and most applicable for the information I've provided. Not sure if anyone can provide a better answer than this. – iLoch Aug 26 '16 at 02:16
1

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.

Luca
  • 11,646
  • 11
  • 70
  • 125
  • Hey Luca, thanks for replying! So I think the issue is a little more complicated than what you've outlined above, because the object I'm trying to pass to C++ (the handle) basically needs to be cast in an unchecked way into an IInspectable (which C# doesn't support.) The reason for this is that the HoloLens branch of ANGLE supports non-HWND "handles" as long as they conform to the requirements (in this case it has to be a PropertySet cast to an IInspectable.) I'd consider doing this in C++ land if it makes it easier. – iLoch Aug 23 '16 at 06:41
  • 1
    Sorry, I didn't notice the ANGLE branch you referenced. However, I doubt that the parameter semantic changes; since the underlying type is IntPtr, the parameter can point to any opaque structure and the value will be passed as is to the linked ANGLE implementation. Maybe I'll check it when I'll come back from my vacation! – Luca Aug 23 '16 at 07:39
  • fyi, all the links are dead – Simon Mourier May 06 '20 at 14:55