Testing N64 emulation...

Greetings everyone !

Last week I ordered some new SDCard to test my new Tinkerboard, and use it on my new phone and… I got a mail from Amazon that the package went back to Amazon for no provided reasons…

So I ordered some new ones this week-end, hoping I get my STUFF THIS TIME, AMAZON !

Meanwhile, I put back the ugly reboot patch for Tinkerboard systems in my build scripts and, since no new kernel have been released this week, I thought about testing the new KMS interface of libSDL2 and give mupen64 another try !

Week-end tinkering

So, getting, compiling, installing and testing the new SDL2 libraries was very easy ! I’m amazed how fluid that went ! The tests worked perfectly and the KMS interface was used all along, without additional post-installation configuration. No Wayland and no X11 were used during the tests. Just pure OpenGL ES through KMS.
Nice !

The compilation went basically like this :

wget "https://www.libsdl.org/release/SDL2-2.0.7.tar.gz"
./configure --enable-video-opengles2 --disable-video-opengl --enable-video-kmsdrm --disable-video-mir --disable-mir-shared
make
make install
cd test
./configure
make
./testgles2

At that point I had a speedy turning colored cube on the screen, with some logs on the SSH session stating that the Mali drivers were being used.

Then I went to test mupen64plus, a Nintendo 64 emulator, and… The biggest issue with emulators projects is that they tend to be broken into little pieces and I never know which one I should use…

So, in order to get something working, I compiled the following projects :

With the following constants exported :

export USE_GLES=1
export NEON=1
export VFP_HARD=1
export VC=0
export NO_ASM=0

Each project must be compiled by going into the generated folder (mupen64plus-bla-bla-bla) and typing :

cd projects/unix
make all
make install

So, I then went to download some N64 ROM for testing purposes. I tried Mario 64 and… The beginning was kind of smooth but the demo with Bowser started to lag terribly when Bowser started to breath flames…

I saw some fragment shader linkage error in the logs, but I don’t know if these were relevant. I tried to fix the issue but it seems that the people who wrote the OpenGL ES shaders in the glide64mk2 renderer are trying to use gl_FragDepth in the fragment shader, which is not defined nor present in OpenGL ES 2.0.
The alternative gl_FragCoord.z being read only, I really don’t know how to fix that…

Still, removing this line and recompiling the renderer didn’t fix the performances issues so I thought about trying some basic Mali driver optimizations.
You can set up the Mali GPU to go full speed constantly by doing the following :

cd /sys/devices/platform/*.gpu/misc/mali0/device/devfreq/devfreq0
echo `cat available_frequencies | cut -f1 -d" "` > min_freq
echo 20 > polling_interval

Launching mupen64plus again with these optimizations gave a WAY smoother gameplay. There were still some serious slowdowns during the fire breathing scene, but not as much as before.

However, these optimizations generate some serious heat on the MiQi, so a good cooling device might be required.

Still, I can say that “it works”. It still requires a ton of optimizations and rework but… it works !

So here it is, with a little tinkering, you could play various emulators with decent speed on your RK3288 devices, using mainline kernels !

EDIT : My mupen64plus-core patch has been applied upstream and is therefore not needed anymore.

Depth testing : Context is everything

I just lost a few hours trying to play with the Z index between draw calls, in order to try Z-layering, as advised by peterharris on ARM Community, in my question For binary transparency : Clip, discard or blend ?.

However, for reasons I did not understand, the Z layer seemed to be completely ignored. Only the glDraw calls order was taken into account.

The solution

Request an EGL_DEPTH_SIZE when selecting a display configuration for your EGL context.

Thought pattern

I really tried everything :

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glClearDepthf(1.0f);
glDepthRangef(0.1f, 1.0f);
glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );

Still… each glDrawArrays drew pixels over previously drawn pixels that had an inferior Z value. Switched the value provided to glDepthFunc, switched the Z values, … same result.

I really started to think that Z-layering only worked for one draw batch…

Until, I searched the OpenGL wiki for “Depth Buffer” informations and stumbled upon Common Mistakes : Depth Testing Doesn’t Work :

Assuming all of that has been set up correctly, your framebuffer may not have a depth buffer at all. This is easy to see for a Framebuffer Object you created. For the Default Framebuffer, this depends entirely on how you created your OpenGL Context.

Not again

After a quick search for EGL Depth buffer on the web, I found the EGL manual page : eglChooseConfig - EGL Reference Pages, which stated this :

EGL_DEPTH_SIZE

Must be followed by a nonnegative integer that indicates the desired depth buffer size, in bits. The smallest depth buffers of at least the specified size is preferred. If the desired size is zero, frame buffer configurations with no depth buffer are preferred. The default value is zero.

The depth buffer is used only by OpenGL and OpenGL ES client APIs.

I should have known…