luca-piccioni / OpenGL.Net

Modern OpenGL bindings for C#.
MIT License
570 stars 109 forks source link

New Sample: Windowless render start #57

Closed kf6kjg closed 6 years ago

kf6kjg commented 7 years ago

This adds a new sample that uses Egl to render a triangle without using any window. This involved some semi-invasive changes to the GetProcAddress class to make the source choosable: X11 is NOT guaranteed to be installed and shouldn't be forced as a dependency when it's not needed.

However there are still some problems to work out:

  1. Egl.cs need to NOT attempt to GetProcAddressX11.GetLibraryHandle("libGLESv2.so", false); under Linux. To solve this I propose finding some way to identify the Raspberry PI as its own platform. As it stands the new sample will crash because it tries to initialize the X11 interface and fails. For testing I'm simply commenting out that line for now.
  2. When running without X11 I still am getting crashes. I'm in the process of hunting that down, seems to be that eglInitialize is returning false for some reason. https://devblogs.nvidia.com/parallelforall/egl-eye-opengl-visualization-without-x-server/ indicates that it should be OK with this...
luca-piccioni commented 7 years ago

This PR prevent execution on Windows platform, as you can see from the unit tests executed in AppVeyor environment.

Unable to initialize base Fixture: System.TypeInitializationException: The type initializer for 'OpenGL.Wgl' threw an exception. ---> System.DllNotFoundException: Unable to load DLL 'libEGL': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
   at OpenGL.GetProcAddressEgl.GetProcAddress(String funcname)
   at OpenGL.KhronosApi.<>c__DisplayClass10_0`1.<BindAPI>b__0(String libpath, String function) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 154
   at OpenGL.KhronosApi.BindAPIFunction(String path, GetAddressDelegate getAddress, FunctionContext functionContext, FieldInfo function, KhronosVersion version, ExtensionsCollection extensions) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 282
   at OpenGL.KhronosApi.BindAPI[T](String path, GetAddressDelegate getAddress, KhronosVersion version, ExtensionsCollection extensions) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 239
   at OpenGL.KhronosApi.BindAPI[T](String path, IGetProcAddress getProcAddress, KhronosVersion version, ExtensionsCollection extensions) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 152
   at OpenGL.KhronosApi.BindAPI[T](String path, IGetProcAddress getProcAddress) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 135
   --- End of inner exception stack trace ---
   at OpenGL.DeviceContextWGL..ctor() in c:\OpenGL.Net\OpenGL.Net\DeviceContextWGL.cs:line 73
   at OpenGL.DeviceContext.Create() in c:\OpenGL.Net\OpenGL.Net\DeviceContext.cs:line 162
   at OpenGL.Test.TestBase.FixtureSetUp() in c:\OpenGL.Net\OpenGL.Net.Test\TestBase.cs:line 45
.F.F.F

To me, it's not clear yet what is causing this, but I suspect that the modifications on GetProcAddress does not take into account all possibilities.


Other clues about your points:

  1. GetProcAddressX11.GetLibraryHandle("libGLESv2.so", false);should be fail-safe (no exception is thrown), and it only loads the library binary, if found. I don't see how this would break execution on Linux platform. Can you provide more information about the error? (i.e. stacktrace and exception info)
  2. Effectively, I've never run EGL on Linux except on RPi. From what I see, you want to use GetProcAddressX11 loader backend on Linux platform, optionally decoupled from X11 method glXGetProcAddress. My current understanding is that it should already be (see GetProcAddressX11 static constructor).
  3. You cannot use EGL loader backend for OS procedure loader: how you can load EGL procedures without dlsym?
  4. I'd prefer to stick with library names completed with extensions, since I don't know if .NET Core is so smart to append the correct extension depending on the running platform.

I think I can try on my RPi by removing X11 to test an equivalent environment.

kf6kjg commented 7 years ago

I saw the integration test error, I'll dig into that when I have another block of time.

Iirc it's the static initializer of GetProcAddressX11 that is throwing an exception. Decoupling the loader could have multiple benefits.

My first goal was to get the example code able to draw. This is done. Second goal is improving compatability. This is next. Final goal is being able to run on a headless minimal server. This I need for some projects I am working on.

On Fri, Aug 11, 2017 at 2:07 AM Luca Piccioni notifications@github.com wrote:

This PR prevent execution on Windows platform, as you can see from the unit tests executed in AppVeyor environment.

Unable to initialize base Fixture: System.TypeInitializationException: The type initializer for 'OpenGL.Wgl' threw an exception. ---> System.DllNotFoundException: Unable to load DLL 'libEGL': The specified module could not be found. (Exception from HRESULT: 0x8007007E) at OpenGL.GetProcAddressEgl.GetProcAddress(String funcname) at OpenGL.KhronosApi.<>c__DisplayClass10_0`1.b__0(String libpath, String function) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 154 at OpenGL.KhronosApi.BindAPIFunction(String path, GetAddressDelegate getAddress, FunctionContext functionContext, FieldInfo function, KhronosVersion version, ExtensionsCollection extensions) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 282 at OpenGL.KhronosApi.BindAPI[T](String path, GetAddressDelegate getAddress, KhronosVersion version, ExtensionsCollection extensions) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 239 at OpenGL.KhronosApi.BindAPI[T](String path, IGetProcAddress getProcAddress, KhronosVersion version, ExtensionsCollection extensions) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 152 at OpenGL.KhronosApi.BindAPI[T](String path, IGetProcAddress getProcAddress) in c:\OpenGL.Net\OpenGL.Net\KhronosApi.cs:line 135 --- End of inner exception stack trace --- at OpenGL.DeviceContextWGL..ctor() in c:\OpenGL.Net\OpenGL.Net\DeviceContextWGL.cs:line 73 at OpenGL.DeviceContext.Create() in c:\OpenGL.Net\OpenGL.Net\DeviceContext.cs:line 162 at OpenGL.Test.TestBase.FixtureSetUp() in c:\OpenGL.Net\OpenGL.Net.Test\TestBase.cs:line 45 .F.F.F

To me, it's not clear yet what is causing this, but I suspect that the modifications on GetProcAddress does not take into account all possibilities.

Other clues about your points:

  1. GetProcAddressX11.GetLibraryHandle("libGLESv2.so", false);should be fail-safe (no exception is thrown), and it only loads the library binary, if found. I don't see how this would break execution on Linux platform.
  2. Effectively, I've never run EGL on Linux except on RPi. From what I see, you want to use GetProcAddressX11 loader backend on Linux platform, optionally decoupled from X11 method glXGetProcAddress. My current understanding is that it should be (see GetProcAddressX11 static constructor).

I think I can try on my RPi by removing X11 to test an equivalent environment.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/luca-piccioni/OpenGL.Net/pull/57#issuecomment-321764015, or mute the thread https://github.com/notifications/unsubscribe-auth/ABGoaMID0J7xeccGIOizAQh54Uf1fNooks5sXBnegaJpZM4O0Rd6 .

luca-piccioni commented 7 years ago

What's wrong with the OffscreenTriangle example? I'm able to create a P-Buffer context via EGL (using ANGLE backend) (the example is not actually working in that configuration, since no ES2 code is implemented).

What is missing is the support for EGL_KHR_surfaceless_context.

kf6kjg commented 7 years ago

The problem is ANGLE. It's a converter from OpenGL to directX, not at all useful on Linux. Note that this example is based on pure OpenGL ES and EGL.

Again my final goal is rendering to Bitmap on a minimal headless Linux, in my case Ubuntu, server. This means no windowing system.

OffscreenTriangle still uses a window, albeit a hidden one. In fact that is one of the issues with OpenGL.NET that I intend to rectify if possible: have the ability to not use any windows, hidden or otherwise.

I'm glad you are asking these questions. I don't expect the PR to be merged soon, but I wanted to start the conversation with a workable starting example that demonstrates the goal. It's definitely not yet finished.

On Fri, Aug 11, 2017 at 9:44 AM Luca Piccioni notifications@github.com wrote:

What's wrong with the OffscreenTriangle example? I'm able to create a P-Buffer context via EGL (using ANGLE backend) (the example is not actually working in that configuration, since no ES2 code is implemented).

What is missing is the support for EGL_KHR_surfaceless_context https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_surfaceless_context.txt .

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/luca-piccioni/OpenGL.Net/pull/57#issuecomment-321862465, or mute the thread https://github.com/notifications/unsubscribe-auth/ABGoaB05F3qZ2SFDOvuM_PE3ZfRBQu3vks5sXIUKgaJpZM4O0Rd6 .

luca-piccioni commented 7 years ago

I understand that ANGLE is a Windows-only solution, but OpenGL.Net is not aware about ANGLE (except for library loading directory patterns). Once OpenGL.Net find an EGL implementation (ANGLE or not), it uses the EGL interface.

For example, I used Egl on RPi using its EGL/ES2 implementation.

In the next days check for any commit on my master branch for supporting EGL_KHR_surfaceless_context. What needs to be modified is DeviceContext.Create()

kf6kjg commented 7 years ago

That'll be good, but I think more still will need changing. We shall see.

What I did was simply use a dummy frame buffer. It's a standard technique, but there are parts of OpenGL.NET that still need change.

I suggest you make a virtual machine with Ubuntu server on your computer and run your code in there. That'll be a fairly good simulation of what I'm attempting to do.

On Fri, Aug 11, 2017 at 10:09 AM Luca Piccioni notifications@github.com wrote:

I understand that ANGLE is a Windows-only solution, but OpenGL.Net is not aware about ANGLE (expect for library loading directory patterns). Once OpenGL.Net find an EGL implementation (ANGLE or not), it uses the EGL interface.

For example, I used Egl on RPi using its EGL/ES2 implementation.

In the next days check for any commit on my master branch for supporting EGL_KHR_surfaceless_context. What needs to be modified is DeviceContext.Create()

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/luca-piccioni/OpenGL.Net/pull/57#issuecomment-321867863, or mute the thread https://github.com/notifications/unsubscribe-auth/ABGoaIN-FKRzo5C3aSvIa89VpYCUJAfkks5sXIqxgaJpZM4O0Rd6 .

luca-piccioni commented 7 years ago

I have an Ubuntu installation, I only need to restore the MBR! 🤣

kf6kjg commented 7 years ago

Just rebased onto master to gain the new good stuff.

Luca, I still get a crash when attempting to execute Egl.cs::82 GetProcAddressLinux.GetLibraryHandle("libGLESv2.so", false); Digging deeper the crash happens at GetProcAddressOS::400 UnsafeNativeMethods.dlopen(string, int). Looks like something's wrong with dlopen.

Commenting out the line at Egl.cs::82, I then crash at Egl.cs::226 in BindAPI(): BindAPI<Egl>(platformLibrary, GetProcAddressOS);, again because that eventually results in a call to dlopen.

Stacktrace:

  at <unknown> <0xffffffff>
  at (wrapper managed-to-native) OpenGL.GetProcAddressLinux/UnsafeNativeMethods.dlopen (string,int) [0x0000b] in <d34fcecfe96f489f8381cb2d074634aa>:0
  at OpenGL.GetProcAddressLinux.GetLibraryHandle (string,bool) [0x00014] in <d34fcecfe96f489f8381cb2d074634aa>:0
  at OpenGL.Egl.Initialize () [0x00077] in <d34fcecfe96f489f8381cb2d074634aa>:0
  at OpenGL.Egl..cctor () [0x00067] in <d34fcecfe96f489f8381cb2d074634aa>:0
  at (wrapper runtime-invoke) object.runtime_invoke_void (object,intptr,intptr,intptr) [0x0001e] in <a07d6bf484a54da2861691df910339b1>:0
  at <unknown> <0xffffffff>
  at WindowlessTriangle.Program.Main () [0x0004d] in <9a8381f65a514dafbbb1d68cf80c1373>:0
  at (wrapper runtime-invoke) object.runtime_invoke_void (object,intptr,intptr,intptr) [0x0004c] in <a07d6bf484a54da2861691df910339b1>:0

......

Native stacktrace:

        mono() [0x4a8617]
        mono() [0x50d696]
        /lib64/ld-linux-x86-64.so.2(+0x110d9) [0x7f328acb60d9]
        [0x7f328ae8e000]

....

=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
=================================================================

Note that the try-catch in BindAPI does not catch this as it's a crash in unmanaged code.

This is one of the things I'd previously attempted to work around by making the code able to choose the library loader based on platform and totally avoid the GetProcAddressLinux class back when it was named GetProcAddressX11. In my rebase I unfortunately lost that code. EDIT: not entirely lost, just found it on an unnamed branch, which I just named for safekeeping. :D

luca-piccioni commented 7 years ago

I'm sorry to hear that. But now things became more clear: I remember that I got a similar problem with mono on linux some year ago: here is my SO question.

I filed a bug to mono , but sadly they have marked it a RESOLVED INVALID (i.e. not a bug), while the problem is evidently in the mono P/Invoke management.

Sadly, avoiding the use GetProcAddressLinux is one way to go, but the correct way to face the problem is to resolve the crash of dlopen, isn't?

Personally I'll try to:

kf6kjg commented 7 years ago

What if you just went with EGL for everything in the EGL operations? That's the premise I was aiming for. There's a whole lot of unneeded code being executed and libraries being loaded that won't be used in this kind of context. Thus I was trying to not only bypass the bugs, but come to a result where the code that causes the bugs isn't even needed in this case as it duplicates what is already available.

On Tue, Aug 15, 2017 at 9:43 PM Luca Piccioni notifications@github.com wrote:

I'm sorry to hear that. But now things became more clear: I remember that I got a similar problem with mono on linux some year ago: here is my SO question https://stackoverflow.com/questions/9954548/sigsegv-when-p-invoking-dlopen .

I filed a bug https://bugzilla.xamarin.com/show_bug.cgi?id=4190 to mono , but sadly they have marked it a RESOLVED INVALID (i.e. not a bug), while the problem is evidently in the mono P/Invoke management.

Sadly, avoiding the use GetProcAddressLinux is one way to go, but the correct way to face the problem is to resolve the crash of dlopen, isn't?

Personally I'll try to:

  • Change Mono environment (upgrading or downgrading): most of the people do use mono successfully.
  • Wrap the dlopen/dlsym in an unmanaged library, just as poupou https://stackoverflow.com/a/9957066/161554 did in his SO answer.

Or, and that could be an options, force the actual GetProcAddressOS.CreateOS() to return a GetGLProcAddressEGL instance. Maybe an environment variable like OPENGL_NET_OSLOADER="EGL" could force the EGL loader as OS loader.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/luca-piccioni/OpenGL.Net/pull/57#issuecomment-322665061, or mute the thread https://github.com/notifications/unsubscribe-auth/ABGoaM9DRxp-RjeV3Iax-xvVYvKfBhGkks5sYnN_gaJpZM4O0Rd6 .

luca-piccioni commented 7 years ago

Indeed, at the current state of the master: if setting environment variable OPENGL_NET_OSLOADER=EGL, the EGL function pointers are loaded with eglGetProcAddress, and well GL function pointers and Gl static initialization. Not tested yet.

However, I have to note that eglGetProcAddress is used to "return a GL or an EGL extension function". Indeed EGL core function are not included; btw I can assume that most EGL implementations of eglGetProcAddress fallback on the default OS loader.

I was thinking to create a "vanilla" version of OpenGL.Net which contains only EGL backend (just like the monoandroid project). And maybe create/find an EGL wrapper with WGL/GLX backends.