Pyrdacor / Ambermoon.net

Ambermoon rewrite in C#
GNU General Public License v3.0
424 stars 22 forks source link

[Bug report] GL Context fails to be created on RPI4/ARM64 #343

Closed midwan closed 1 year ago

midwan commented 1 year ago

Describe the bug Ambermoon fails to start on a Raspberry Pi running Manjaro (arm64/aarch64), with the following error:

Could not create GL context: GLXBadFBConfig
   at Silk.NET.SDL.Sdl.ThrowError()
   at Silk.NET.SDL.SdlContext.Create(ValueTuple`2[] attributes)
   at Silk.NET.Windowing.Sdl.SdlView.CoreInitialize(ViewOptions opts, Nullable`1 additionalFlags, Nullable`1 x, Nullable`1 y, Nullable`1 w, Nullable`1 h, String title, IGLContext sharedContext)
   at Silk.NET.Windowing.Sdl.SdlWindow.CoreInitialize(ViewOptions opts)
   at Silk.NET.Windowing.Internals.ViewImplementationBase.Initialize()
   at Silk.NET.Windowing.WindowExtensions.Run(IView view)
   at Ambermoon.GameWindow.Run(Configuration configuration)
   at Ambermoon.Program.Main()
Press return to exit

The window opens up, but contains nothing. Pressing enter in the console shuts down the game.

Based on this related answer in StackOverflow: https://stackoverflow.com/questions/65531911/glx-context-creation-error-glxbadfbconfig, the specification of GLX_ARB_create_context is clear about when GLXBadFBConfig error may be returned:

If <config> does not support compatible OpenGL contexts
    providing the requested API major and minor version,
    forward-compatible flag, and debug context flag, GLXBadFBConfig
    is generated.

It might be a case of Silk.NET requesting a major/minor version that is not supported on these devices, but I'm not 100% sure yet.

The output of glxinfo | grep -e "OpenGL version" -e "Core" -e "Compatible" on the device is the following:

OpenGL version string: 2.1 Mesa 22.3.4

From what I could see so far, you don't explicitly request any GL version when initializing the window, so this may be internal to Silk.NET. I'll try opening an issue there to see if they have any feedback, and keep digging to see if there's a way to fix this.

Submitting this issue here for future reference, and in the case of any PR that fixes it. :)

midwan commented 1 year ago

Related issue at Silk.NET: https://github.com/dotnet/Silk.NET/issues/1277

midwan commented 1 year ago

@Pyrdacor based on the replies on the Silk.NET issue, and from what I tested here, it seems that this library won't work on devices such as the RPI, due to the OpenGL minimum requirements (the lib is hardcoded to use OpenGL 3.1 as a minimum).

Considering there are no 3D functions being used, perhaps we could use something simpler like a pure SDL2 implementation? Not sure how much of a change that would be...

Pyrdacor commented 1 year ago

Well OpenGL 3.1 is explicitly required as the engine uses shaders. This is from 2009 so 14 years old already. But the Pi uses OpenGL ES which is a whole different story. See the Ambermoon.net project and there the GameWindow.cs file. Relevant code:

#if GLES
            var api = new GraphicsAPI
                (ContextAPI.OpenGLES, ContextProfile.Compatability, ContextFlags.Default, new APIVersion(3, 0));
#else
            var api = GraphicsAPI.Default;
#endif

So to make it work with Pi you need to define GLES when compiling.

The Pi4 should support OpenGL ES 3.0 which is based on OpenGL 3.3. This is what I am aiming for. The Pi3 only has OpenGL ES 2.0 which won't work maybe. But I guess it also emulates some newer features as well.

But as I just now realize, it also supports at least fragment and vertex shaders which is all that is needed. So maybe just change new APIVersion(3, 0) to new APIVersion(2, 0) and compile with GLES defined. Might work.

Pyrdacor commented 1 year ago

Creating a new render backend with SDL is a huge task. But in theory I designed the projects to be able to support this. Basically the game works with interfaces defined in the Ambermoon.Core project in sub-folder "Render".

You could create your own render backend which implements those interfaces and provides the implementation.

Basically you have to take the project Ambermoon.Renderer.OpenGL as a reference and create a new similar project.

Still it is a lot of work. But this would be the way to go. As you can see in that project, there are many shader classes which won't work without OpenGL 3.1 or OpenGL ES 2 or 3.

The concept in a nutshell is:

If you implement them all, you should be fine in theory. The project grew a lot since the initial design so there might be some more things to take care of.

In general it should be enough to set the Visible property of a render node to true and ensure a valid position on the screen through the X and Y property. And of course a valid layer must be assigned. If those 4 properties are valid, the render node is drawn. This is the basic workflow for displaying stuff on the screen.

I could provide more info, but writing a new renderer might be much work.

midwan commented 1 year ago

I tried the configuration ReleaseWithAndroid, which seems to define GLES already, and that seems to go on a bit further, but eventually fails with a different error:

[Render] Unable to create layer 'Map3DBackground': 0:19(18): error: no matching function for call to `mod(int, int)'; candidates are:
0:19(18): error:    float mod(float, float)
0:19(18): error:    vec2 mod(vec2, float)
0:19(18): error:    vec3 mod(vec3, float)
0:19(18): error:    vec4 mod(vec4, float)
0:19(18): error:    vec2 mod(vec2, vec2)
0:19(18): error:    vec3 mod(vec3, vec3)
0:19(18): error:    vec4 mod(vec4, vec4)
0:19(14): error: cannot construct `int' from a non-numeric data type
0:20(13): warning: `index' used uninitialized
0:20(27): warning: `index' used uninitialized
0:21(13): warning: `index' used uninitialized
   at Ambermoon.Renderer.OpenGL.RenderView..ctor(IContextProvider contextProvider, IGameData gameData, IGraphicProvider graphicProvider, ITextProcessor textProcessor, Func`1 textureAtlasManagerProvider, Int32 framebufferWidth, Int32 framebufferHeight, Size windowSize, Boolean& useFrameBuffer, Boolean& useEffectFrameBuffer, Func`1 screenBufferModeProvider, Func`1 effectProvider, Graphic[] additionalPalettes, DeviceType deviceType, SizingPolicy sizingPolicy, OrientationPolicy orientationPolicy)
   at Ambermoon.GameWindow.CreateRenderView(GameData gameData, IConfiguration configuration, GraphicProvider graphicProvider, FontProvider fontProvider, Graphic[] additionalPalettes, Func`1 textureAtlasManagerProvider)
   at Ambermoon.GameWindow.ShowVersionSelector(BinaryReader builtinVersionReader, Action`4 selectHandler, TextureAtlasManager& createdTextureAtlasManager)
   at Ambermoon.GameWindow.Window_Load()
   at Silk.NET.Windowing.Internals.ViewImplementationBase.Initialize()
   at Silk.NET.Windowing.WindowExtensions.Run(IView view)
   at Ambermoon.GameWindow.Run(Configuration configuration)
   at Ambermoon.Program.Main()

it seems that's the shader itself that fails now, but I don't have enough knowledge about shaders to fix that :)

BTW, the UseGLES option you passed from PowerShell doesn't seem to work for me. It would never enable/define GLES in the project, so I ended up using the ReleaseWithAndroid configuration instead.

Pyrdacor commented 1 year ago

For the first shader error you could fix this with changing mod(x, y) to mod(float(x), float(y)).

Maybe you have to do int(mod(float(x), float(y))).

Maybe ES doesn't support some functions.

If this is the only error, this is no big deal.

midwan commented 1 year ago

@Pyrdacor Thanks! I managed to make some progress with your suggestions above, by changing one reference in the shader. Now the game starts up with no errors (besides the warning about the window as shown above), and I can get to the point where I start a new game, but the character selection screen is... problematic:

image

Any idea what this might be related to?

midwan commented 1 year ago

Also, I am probably missing something here, but I can't seem to be able to define GLES as expected in the Ambermoon.net project. For now I'm just commenting out the alternative to compile for the RPI, but ideally this should work properly...

image

You had an env variable being defined in your PS1 script, but that didn't seem to work. No matter what value it got (true/false) it would never define GLES, even though you had that condition in the project file. I used the ReleaseWithAndroid config that the Ambermoon.Renderer.OpenGL had, and that one defines GLES when it's selected, so I tried creating that config in the Ambermoon.net project as well:

    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithAndroid|AnyCPU'">
        <DefineConstants>$(DefineConstants);GLES</DefineConstants>
        <Optimize>True</Optimize>
    </PropertyGroup>

However, it still does not define GLES for me when I select that config, at least not in Ambermoon.net - it does in Ambermoon.Renderer.OpenGL as expected. :-/

Like I said, I'm probably missing something here, but I think this should have worked.

Pyrdacor commented 1 year ago

The Ambermoon.net project most likely has no such configuration. You would have to add it. You can see this in the configuration manager of the solution. I bet when the solution configuration "ReleaseWithAndroid" is selected, the Ambrrmoon.net project uses the "Release" config.

The configurations exist for each project and also for the whole solution. But a project can use a different configuration as the solution config. Especially when it doesn't have that config.

Pyrdacor commented 1 year ago

About the messed up UI. This can have multiple causes. One is the display layer (layer in the shader) which controls the Z order.

Pyrdacor commented 1 year ago

You can check in the debugger if the property Visible of the sprites is true. If yes it is some shader issue. Might be rounding or something like that.

midwan commented 1 year ago

The Ambermoon.net project most likely has no such configuration. You would have to add it. You can see this in the configuration manager of the solution. I bet when the solution configuration "ReleaseWithAndroid" is selected, the Ambrrmoon.net project uses the "Release" config.

The configurations exist for each project and also for the whole solution. But a project can use a different configuration as the solution config. Especially when it doesn't have that config.

I think I have it correctly set up - The solution has the correct configs selected per project: image

And then in Ambermoon.net I added the new config:

<Configurations>Debug;Release;DebugAndroid;ReleaseNative;ReleaseWithAndroid</Configurations>

and finally tried to have it set the constant when it's selected:

    <PropertyGroup Condition="'$(Configuration)'=='ReleaseWithAndroid'">
        <DefineConstants>GLES</DefineConstants>
        <Optimize>True</Optimize>
    </PropertyGroup>

(tried both with just a Configuration and also a combination of Configuration/CPU)

While the same seems to work for the OpenGL project, it does not for the Ambermoon.net one. Weird...

I'll check for any clues regarding the character selection screen and get back.

midwan commented 1 year ago

Regarding the config issue above, perhaps you can take a look at my branch and see if you can notice something I missed: https://github.com/midwan/Ambermoon.net/tree/fix-rpi :)

Pyrdacor commented 1 year ago

How did you check if GLES is defined? As it is only evaluated in the renderer project it won't have an effect to define it in Ambermoon.net I guess.

Pyrdacor commented 1 year ago

In the configuration manager I see "ReleaseWithAndroid" but in the project file you added and also check for "ReleaseAndroid"

midwan commented 1 year ago

@Pyrdacor It's also evaluated in GameWindow.cs of the Ambermoon.net project:

        public void Run(Configuration configuration)
        {
            this.configuration = configuration;
            var screenSize = configuration.GetScreenSize();
            Width = screenSize.Width;
            Height = screenSize.Height;

#if GLES
            var api = new GraphicsAPI
                (ContextAPI.OpenGLES, ContextProfile.Compatability, ContextFlags.Default, new APIVersion(3, 0));
#else
            var api = GraphicsAPI.Default;
#endif
            var version = Assembly.GetExecutingAssembly().GetName().Version;
            gameVersion = $"Ambermoon.net v{version.Major}.{version.Minor}.{version.Build}";
            var videoMode = new VideoMode(60);
            var options = new WindowOptions(true, new WindowDimension(100, 100),
                new WindowDimension(Width, Height), 60.0, 60.0, api, gameVersion,
                WindowState.Normal, WindowBorder.Fixed, true, false, videoMode, 24);
            options.WindowClass = "Ambermoon.net";

I didn't add that, it was already there. And you had a condition in the project file to define GLES if the variable UseGLES was used, but that didn't seem to work (at least not for me)...

Pyrdacor commented 1 year ago

Ok good catch. You saw my second reply? You used the wrong configuration name in the project file.

midwan commented 1 year ago

@Pyrdacor I did a search for ReleaseWithAndroid in all files and replaced any remaining occurrences now - but it still refuses to show GLES as defined in that file. It's like something is undefining it, very strange. If I add a #define GLES at the top of the file, it works of course. But for some reason it doesn't pick it up from the project file.

Pyrdacor commented 1 year ago

I think you should instead have replaced "ReleaseAndroid" by "ReleaseWithAndroid" as this is the used solution configuration. If it works in the renderer project it should also work in the main project if the names are right.

Pyrdacor commented 1 year ago

Ok I saw you also renamed it inside the solution file so it should work. Did you restart VS? Sometimes it's just a visual issue and it actually works. I can test it tomorrow when I am back at my PC.

Pyrdacor commented 1 year ago

If you want to rule out the configuration you could just remove the condition in the project file for testing. If it then still does not work, something else is broken.

Pyrdacor commented 1 year ago

Ok I found the reason. The project has another condition <PropertyGroup Condition="'$(OS)' == 'Windows_NT'"> which adds the "WINDOWS" constant. But this overrides all other constants on Windows as it just sets this one constant. It should be <DefineConstants>$(DefineConstants);WINDOWS</DefineConstants> there to keep the previous ones.

I updated the whole solutions and projects and added 2 new configs "ReleaseES" and "DebugES" to distinguish between Android and just ES (RPI etc).

Maybe you can just rebase against master again.

I already changed the ES context version to 2.0, upgraded some dependencies etc.

See a51b905b89df43f3cd21134f31bae46daa208812

midwan commented 1 year ago

Ah, good catch!

I'll rebase against master again, sure. Just a note: OpenGLES 3.0 seemed to work on the RPI, so perhaps you don't need to take it down to 2.0. ;-)

Pyrdacor commented 1 year ago

How is the status? I saw on your screenshot in the background that it runs with the SDL backend which is not well supported. Were you able to run it with glow?

midwan commented 1 year ago

I didn't have a chance to do anything more with this, I'm afraid. Been busy with a few other projects lately...

Pyrdacor commented 1 year ago

Ok let me know when you continue with this.

midwan commented 1 year ago

Maybe we can try this with a Pull request, so we can comment/make changes in the code directly as well? I can send over what I have done so far, and we can continue on that.

midwan commented 1 year ago

PR submitted: https://github.com/Pyrdacor/Ambermoon.net/pull/346 - ignore that, I'll clean up the git mess and try again

midwan commented 1 year ago

Proper PR submitted: https://github.com/Pyrdacor/Ambermoon.net/pull/347 :)

midwan commented 1 year ago

Perhaps this issue should be closed, as we've moved on from the original problem/description? :)