gonetz / GLideN64

A new generation, open-source graphics plugin for N64 emulators.
Other
754 stars 174 forks source link

Blank video output with Mupen64plus (GLES2/Raspberry Pi) #2837

Open cmitu opened 1 month ago

cmitu commented 1 month ago

Hello,

starting with commit 1a0621ddf4a70835e9b38cf5a326cbb34d7067aa (bisected via git bisecgt), there's no video output on some N64 titles (i.e Cruis'n USA). There's sound and the input (keyboard in my case) still works, but there's a blank/black video output. It may be related to the device/renderer used - a Rpi4 with GLES3, also reported to have issues on the Pi5. OS is current RaspiOS (based on Debian 12 bookworm, current stable) on 64bit ARM (aarch64). The log doesn't show much, see below.

Mupen64plus log ``` Mupen64Plus Console User-Interface Version 2.5.9 UI-Console: attached to core library 'Mupen64Plus Core' version 2.5.9 UI-Console: Includes support for Dynamic Recompiler. Core: Using full mem base Core: Goodname: Cruis'n USA (U) (V1.2) [!] Core: Name: Cruis'n USA Core: MD5: 2838A9018AD2BCB8B7F6161C746A1B71 Core: CRC: B3402554 7340C004 Core: Imagetype: .z64 (native) Core: Rom size: 8388608 bytes (or 8 Mb or 64 Megabits) Core: Version: 1444 Core: Manufacturer: Nintendo Core: Country: USA UI-Console Status: Cheat codes disabled. UI-Console: using Video plugin: 'GLideN64 rev.b021d8e' v2.0.0 UI-Console: using Audio plugin: 'Mupen64Plus SDL Audio Plugin' v2.5.9 Input: Using auto-config file at: '/opt/retropie/configs/n64/InputAutoCfg.ini' UI-Console: using Input plugin: 'Mupen64Plus SDL Input Plugin' v2.5.9 UI-Console: using RSP plugin: 'Hacktarux/Azimer High-Level Emulation RSP Plugin' v2.5.9 Core Warning: Input plugin does not contain VRU support. Core: input plugin did not specify a render callback; there will be no on screen display by the input plugin. Input: 0 SDL joysticks were found. Input: N64 Controller #1: Forcing default keyboard configuration Input: Using auto-config file at: '/opt/retropie/configs/n64/InputAutoCfg.ini' Input: 1 controller(s) found, 1 plugged in and usable in the emulator Input Warning: Joystick #1 doesn't support rumble effect Input Warning: Joystick #2 doesn't support rumble effect Input Warning: Joystick #3 doesn't support rumble effect Input Warning: Joystick #4 doesn't support rumble effect Input: Mupen64Plus SDL Input Plugin version 2.5.9 initialized. RSP: RSP Fallback disabled ! Core: Using video capture backend: dummy Core: Game controller 0 (Standard controller) has a Memory pak plugged in Core: Game controller 1 (Standard controller) has a Memory pak plugged in Core: Game controller 2 (Standard controller) has a Memory pak plugged in Core: Game controller 3 (Standard controller) has a Memory pak plugged in Core: Using CIC type X102 Core: Setting video mode: 1280x1024 Audio: Using resampler speex Audio: Initializing SDL audio subsystem... Input Warning: Joystick #1 doesn't support rumble effect Input Warning: Joystick #2 doesn't support rumble effect Input Warning: Joystick #3 doesn't support rumble effect Input Warning: Joystick #4 doesn't support rumble effect Core: Initializing 4 RDRAM modules for a total of 8 MB Core: Starting R4300 emulator: Dynamic Recompiler Core: Init new dynarec Audio: Initializing SDL audio subsystem... ^[Core Status: Stopping emulation. Core: R4300 emulator finished. Core Status: Rom closed. ```

Is there anything that could be added to the config to increase the logging or to better diagnose the issue ?

Jj0YzL5nvJ commented 1 month ago

Try with: export MESA_GLSL_CACHE_DISABLE=1

Or clear your shader cache. rm -rf ${HOME}/.cache/mupen64plus ${HOME}/.cache/mesa_shader_cache

cmitu commented 1 month ago

No, ignoring/erasing the shader cache doesn't seem to help - I still get a black screen. For a couple of launches, before invalidating the cache, I got a video output. Then, for the subsequent launches, using MESA_GLSL_CACHE_DISABLE=1 and clearing the cache, I got the same black screen.

Jj0YzL5nvJ commented 1 month ago

Then seek for gliden64.log in the mupen64plus configuration folder.

cmitu commented 1 month ago

Thanks. The gliden64.log file shows:

2024/05/08,04:09:38.981,opengl_Utils.cpp:60,WARNING, "Could not query EGL extensions on this device"
2024/05/08,04:09:38.981,opengl_Utils.cpp:60,WARNING, "Could not query EGL extensions on this device"

and previously also printed:

2024/05/07,19:27:00.746,glsl_ShaderStorage.cpp:191,ERROR, "Error while writing shader with key key=0x3811FE23FFFFF7FB"
2024/05/07,19:27:00.746,glsl_ShaderStorage.cpp:191,ERROR, "Error while writing shader with key key=0x3851D6A3FFAFFFFF"
2024/05/07,19:27:00.746,glsl_ShaderStorage.cpp:191,ERROR, "Error while writing shader with key key=0x38FFFFFFFFFDF6FB

I think the latter messages have been printed after I cleared the shader cache, the former are printed right now when I start and get a blank video output.

scurest commented 1 month ago

I can repro the screen being fully black if I hack the snoise functions to always return 0.0. I suspect this is what's happening.

My guess is the GLSL mediump precision on the RPi is actually a float16. From a quick test, it looks like float16s don't have enough precision for the hash function to work (you ends up the fractional part of a number in the range where all fractional parts are 0). This would also explain why it doesn't happen on desktop, where everything is float32.

Can you see if this fixes it?

diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
index 901d17ab..1de49ab1 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
@@ -1007,8 +1007,8 @@ public:
            "uniform float uNoiseSeed;                              \n"
            "lowp float snoise()                                    \n"
            "{                                                      \n"
-           "  mediump vec2 coord = floor(gl_FragCoord.xy/uScreenScale);    \n"
-           "  mediump vec3 p3 = vec3(uNoiseSeed, coord);           \n"
+           "  highp vec2 coord = floor(gl_FragCoord.xy/uScreenScale);  \n"
+           "  highp vec3 p3 = vec3(uNoiseSeed, coord);         \n"
            // hash13 from https://www.shadertoy.com/view/4djSRW
            "  p3 = fract(p3 * .1031);                              \n"
            "  p3 += dot(p3, p3.zyx + 31.32);                       \n"
@@ -1063,13 +1063,13 @@ public:
        if (config.generalEmulation.enableHiresNoiseDithering != 0)
            // multiplier for higher res noise effect
            m_part +=
-           "  lowp float mult = 1.0 + step(2.0, uScreenScale.x);   \n";
+           "  highp float mult = 1.0 + step(2.0, uScreenScale.x);  \n";
        else
            m_part +=
-           "  lowp float mult = 1.0;                               \n";
+           "  highp float mult = 1.0;                              \n";
        m_part +=
-           "  mediump vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale));   \n"
-           "  mediump vec3 p3 = vec3(uNoiseSeed, coord);               \n"
+           "  highp vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale)); \n"
+           "  highp vec3 p3 = vec3(uNoiseSeed, coord);             \n"
            // hash33 from https://www.shadertoy.com/view/4djSRW
            "  p3 = fract(p3 * vec3(.1031, .1030, .0973));              \n"
            "  p3 += dot(p3, p3.yxz+33.33);                             \n"
@@ -1081,14 +1081,14 @@ public:
        if (config.generalEmulation.enableHiresNoiseDithering != 0)
            // multiplier for higher res noise effect
            m_part +=
-           "  lowp float mult = 1.0 + step(2.0, uScreenScale.x);   \n";
+           "  highp float mult = 1.0 + step(2.0, uScreenScale.x);  \n";
        else
            m_part +=
-           "  lowp float mult = 1.0;                               \n";
+           "  highp float mult = 1.0;                              \n";
        m_part +=
            "                                                       \n"
-           "  mediump vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale));   \n"
-           "  mediump vec3 p3 = vec3(uNoiseSeed, coord);           \n"
+           "  highp vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale)); \n"
+           "  highp vec3 p3 = vec3(uNoiseSeed, coord);         \n"
            // hash13 from https://www.shadertoy.com/view/4djSRW
            "  p3 = fract(p3 * .1031);                              \n"
            "  p3 += dot(p3, p3.zyx + 31.32);                       \n"
cmitu commented 1 month ago

@scurest thank you for the patch. Unfortunately it doesn't resolve the issue - still getting a black video. Here's a MESA shader cache dump taken from the start-up - https://gist.github.com/cmitu/57bd70c14e9f6914a96732f5844f1769.

Jj0YzL5nvJ commented 1 month ago

@scurest, can you see #2507 too? I'm available as a guinea pig.

scurest commented 1 month ago

@cmitu Okay, thanks. I notice that if the shader has compilation errors it also produces a black screen. Compilation errors only get logged to gliden64.log in debug builds, ie. you need to pass -DCMAKE_BUILD_TYPE=Debug when you run cmake. Can you make sure you're using a debug build?

You can test whether logging of shader compilation errors is working with a patch like this

diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
index 901d17ab..e04c05bd 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
@@ -1073,7 +1073,7 @@ public:
            // hash33 from https://www.shadertoy.com/view/4djSRW
            "  p3 = fract(p3 * vec3(.1031, .1030, .0973));              \n"
            "  p3 += dot(p3, p3.yxz+33.33);                             \n"
-           "  return fract((p3.xxy + p3.yxx)*p3.zyx);                  \n"
+           "  return adsfhdsakjhfjlksahliurhsadsakjdfhl;               \n"
            "}                                                          \n"
            "lowp float snoiseA()                                   \n"
            "{                                                      \n"

which will definitely trigger an error.

Also make sure to clear the shader cache before testing.

@Jj0YzL5nvJ I don't understand the connection to this issue.

Jj0YzL5nvJ commented 1 month ago

I don't understand the connection to this issue.

It's very likely a float precision issue as well. That hardware originally only had support for OpenGL 2.0, but later received support for OpenGL 3.3 in software.

It works with EnableInaccurateTextureCoordinates=True but fails with EnableInaccurateTextureCoordinates=False, and not generate errors, I suspect the driver might be truncating the precision of some shaders to maintain performance.

If it were possible to know exactly what the driver doesn't like, a special path/hack could be created for that hardware.

cmitu commented 1 month ago

Can you make sure you're using a debug build?

Thank you. I've switched to a Debug build and the behavior/logging is changed.

Running the emulator crashes outright because of an assert:

....
Input: Mupen64Plus SDL Input Plugin version 2.5.9 initialized.
RSP: RSP Fallback disabled !
Core Status: Selected state slot: 3
Core: Using video capture backend: dummy
Core: Game controller 0 (Standard controller) has a Memory pak plugged in
Core: Game controller 1 (Standard controller) has a Memory pak plugged in
Core: Game controller 2 (Standard controller) has a Memory pak plugged in
Core: Game controller 3 (Standard controller) has a Memory pak plugged in
Core: Using CIC type X102
Core: Setting video mode: 1280x1024

mupen64plus: /home/pi/RetroPie-Setup/tmp/build/mupen64plus/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp:514: graphics::CombinerProgram* glsl::CombinerProgramBuilder::buildCombinerProgram(Combiner&, Combiner&, const CombinerKey&): Assertion `Utils::checkProgramLinkStatus(program)' failed.

The gliden64.log log file contains the shader linking error - https://pastebin.com/9HV0SBqJ

scurest commented 1 month ago

Please try

diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
index 901d17ab..5ef84042 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
@@ -1004,11 +1004,11 @@ public:
    ShaderNoise(const opengl::GLInfo & _glinfo)
    {
        m_part =
-           "uniform float uNoiseSeed;                              \n"
+           "uniform mediump float uNoiseSeed;                      \n"
            "lowp float snoise()                                    \n"
            "{                                                      \n"
-           "  mediump vec2 coord = floor(gl_FragCoord.xy/uScreenScale);    \n"
-           "  mediump vec3 p3 = vec3(uNoiseSeed, coord);           \n"
+           "  highp vec2 coord = floor(gl_FragCoord.xy/uScreenScale);  \n"
+           "  highp vec3 p3 = vec3(uNoiseSeed, coord);         \n"
            // hash13 from https://www.shadertoy.com/view/4djSRW
            "  p3 = fract(p3 * .1031);                              \n"
            "  p3 += dot(p3, p3.zyx + 31.32);                       \n"
@@ -1068,8 +1068,8 @@ public:
            m_part +=
            "  lowp float mult = 1.0;                               \n";
        m_part +=
-           "  mediump vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale));   \n"
-           "  mediump vec3 p3 = vec3(uNoiseSeed, coord);               \n"
+           "  highp vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale)); \n"
+           "  highp vec3 p3 = vec3(uNoiseSeed, coord);             \n"
            // hash33 from https://www.shadertoy.com/view/4djSRW
            "  p3 = fract(p3 * vec3(.1031, .1030, .0973));              \n"
            "  p3 += dot(p3, p3.yxz+33.33);                             \n"
@@ -1087,8 +1087,8 @@ public:
            "  lowp float mult = 1.0;                               \n";
        m_part +=
            "                                                       \n"
-           "  mediump vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale));   \n"
-           "  mediump vec3 p3 = vec3(uNoiseSeed, coord);           \n"
+           "  highp vec2 coord = floor(mult * (gl_FragCoord.xy/uScreenScale)); \n"
+           "  highp vec3 p3 = vec3(uNoiseSeed, coord);         \n"
            // hash13 from https://www.shadertoy.com/view/4djSRW
            "  p3 = fract(p3 * .1031);                              \n"
            "  p3 += dot(p3, p3.zyx + 31.32);                       \n"
cmitu commented 1 month ago

@scurest Thank you for the patch - after applying it now video output shows up, no longer a black screen.