Failed to link lighting shaders on Android

Reading the topic linked above indicates that the initial fix wasn’t quite correct. Apparently @nehon and @joliver82 planned to follow up, but perhaps they never did, which would explain why master is still broken.

Hi all,

@sgold, in fact I’ve been working in my own fork of jme3 with some light improvements for jme3 for android but sadly they’re still not mature enough for PR.

About the issue here, it doesn’t look like the one detected in the linked topic which in fact, as far as I know, it was never fully fixed in master, the change was applied to version >= 130 instead of the “else” path, so shadowing should still be failing for android because the engine is always using gles 2.0 with shaders 1.0

Getting back to the topic… @tharg2, I didn’t undertand your last message… did it work for you in master or are you still having the same error?

1 Like

Hi @joliver82 Sorry it is getting a little confused with the 2 errors being discussed.

Re. the ‘precision’ error: Error: uniform m_Shininess specified with different precision in different shaders.

  1. setHardwareSkinningPreferred(false); => shadowless jaime works on my android tablet
  2. setHardwareSkinningPreferred(false); => shadowless jaime crashes on the android emulator with the ‘precision’ error
  3. setHardwareSkinningPreferred(false) AND VertexLighting=true => shadowless jaime works in the emulator

Re. the linked error: PostShadow.frag fails to compile:
Occurs on both real tablet and emulator when I enable shadowing, via setShadowMode

This tends to imply that shadows are using a feature not available on your tablet/emulator/etc.

@pspeed Yup. The “extension is not supported” is pretty explicit. So are shadows impossible on Android? Should I try later versions of Android platform on the Android emulator?
Yesterday I didn’t know what a shader was. Today I still don’t :wink:
It’s still a mystery where the cutoff lies between JME software vs Android system software vs Android hardware. Some simplistic architecture diagrams might help me understand where the Shady Shader fits in.
I’ll go read some more…

.vert and .frag files are compiled into binary code that is run on the GPU. It’s the graphics drivers that compile it.

You can read more about it at “pretty much any OpenGL site on the web”.

Thanks, becoming clearer.

I got TestJaime.java working on Android.
Here follows a list of problems I found and solutions.

I tested this on an ancient bottom-of-the-range generic Android tablet
In Android Studio, the device displays as “Unknown A13 (Android 4.1.1, API 15)”
Jaime jumps up and down with a nice shadow below him.
The only thing not working is the floor spotlight reflection.

I then tested on a Samsung Galaxy Tab E.
Jaime jumps up and down with NO shadow below him.
The floor spotlight reflection is not working.

Also tested in an Android Emulator
All working including the floor spotlight reflection.

The modified TestJaime.java still works on Windows 10 SDK 3.2.2 Stable.

Problem 1.
On the old Android tablet the screen is a jumble of brown triangles.

Solution 1.
Change TestJaime.java
//disable hardware skinning, else on Android just see a screen full of random brown triangles
SkeletonControl sc = jaime.getControl(SkeletonControl.class);
sc.setHardwareSkinningPreferred(false);

Problem 2.
Get the following crash on Android real device AND emulator:
2019-03-18 12:29:00.741 11840-11861/com.tharg.basicgame2 E/com.jme3.app.AndroidHarness: SEVERE Exception thrown in Thread[GLThread 1275,5,main]
com.jme3.renderer.RendererException: compile error in: ShaderSource[name=Common/MatDefs/Shadow/PostShadow.frag, defines, type=Fragment, language=GLSL100]
WARNING: 0:8: ‘GL_ARB_gpu_shader5’ : extension is not supported
ERROR: 0:118: ‘sampler2DShadow’ : Illegal use of reserved word
ERROR: 0:118: ‘sampler2DShadow’ : syntax error

Solution 2.
Change TestJaime.java
shadows.setShadowCompareMode(CompareMode.Software);
This sets HARDWARE_SHADOWS to false in shadows.glsllib and avoids use of the unsupported extension.
[NB. Is it better to introduce this extension with an #ifdef setup by GLRenderer capabilities test??]

Problem 3.
java.lang.IllegalStateException: Framebuffer has erronous attachment.
at com.jme3.renderer.opengl.GLRenderer.checkFrameBufferError(GLRenderer.java:1636)

Solution 3.
Change TestJaime.java
Comment out the FilterPostProcessor code block, which adds SSAOFilter to the viewport.
It doesn’t work on Android. Don’t know why. Doesn’t seem to do much anyway ???

Problem 4.
com.jme3.renderer.RendererException: Shader failed to link, shader:Shader[numSources=2, numUniforms=18, numBufferBlocks=0, shaderSources=[ShaderSource[name=Common/MatDefs/Light/Lighting.frag, defines, type=Fragment, language=GLSL100], ShaderSource[name=Common/MatDefs/Light/Lighting.vert, defines, type=Vertex, language=GLSL100]]]
Error: uniform m_Shininess specified with different precision in different shaders.
at com.jme3.renderer.opengl.GLRenderer.updateShaderData(GLRenderer.java:1497)…

Solution 4.
Change Lighting.vert and Lighting.frag

m_shininess is declared as highp in the vert shader (by default)
m_shininess precision is not declared highp in the frag shader.

So the solution is to explicitly state the precision of
uniform float m_shininess; in Lighting.vert and Lighting.frag.
I chose:
uniform mediump float m_Shininess;

Problem 5. No Shadows on later model Tablet - NO SOLUTION YET
I tried my working modified app above on a Samsung Galaxy Tab E.
Jaimie appeared doing his dance, but had no shadow.
The Tablet details are listed below:
* Android 7.1.1
* Vendor: Qualcomm
* Renderer: Adreno ™ 306
* OpenGL Version: OpenGL ES 3.0 V@145.0 AU@ (GIT@I750f7f2fa6)
* GLSL Version: OpenGL ES GLSL ES 3.00
* Profile: Compatibility
I went through PostShadow.frag and added the highp precision qualifier to
float, vec2 and vec4 declarations. Still no shadow.
(NB. highp and mediump: no difference)
I called setHardwareSkinningPreferred(true). App didn’t crash but still no shadow.
I Called setShadowCompareMode(CompareMode.Hardware); App crashed as above.

Problem 6. No floor spotlight reflection - NO SOLUTION YET
The floor spotlight reflection is not working on Android devices.
It works on an Android emulator on windows, and in the JME SDK.
It only works with the original ‘Quad’ Geometry:
Quad q = new Quad(20, 20);
q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(10));
If the floor material is changed to a flat box, it doesn’t work at all.
I have no idea :expressionless:

TBD:

If anyone can explain how best to debug the shaders I could do some more.
There are a plethora of #ifdef and #ifndef in the shaders.
I’d just like to identify the route through these, with a view to identifying the shadow call which doesn’t work.
Or maybe just revert to the old Tablet ‘route’ for now, which amazingly provides working shadows.
Is there any support for logging?
I read that someone displayed different coloured pixels on the screen as an aid to debuging…
Any help welcome…

Bumping this up. Just got the original question error on an emulator.

Did you try the solutions provided?

Yeah, the solution from tharg2 worked:

Solution 4.
Change Lighting.vert and Lighting.frag
m_shininess is declared as highp in the vert shader (by default)
m_shininess precision is not declared highp in the frag shader.

So the solution is to explicitly state the precision of
uniform float m_shininess; in Lighting.vert and Lighting.frag.
I chose:
uniform mediump float m_Shininess;

Did you try jayfella’s solution of being specific about shader version?

This seems like an “emulator” issue related to the automatic version selection.

So, just tried:

  • leaving only GLSL100 for all the places in the j3md file yields absolutely the same error
  • specifying anything above GLSL100, like GLSL110 gives something like
    java.lang.UnsupportedOperationException: No technique 'Default' on material 'Phong Lighting' is supported by the video hardware. The capabilities [GLSL150] are required.

PS I can try on different emulator configs if you want, but I’m using the default one currently, that was pre-installed with AndroidStrudio. And there’s not like many things to change… I’m just surprised that it can’t find anything above GLSL100 :confused:

GLSL110 is a desktop version.

GLSL100 is not a version at all. JME treats that as “hunt for an appropriate version”… it used to be “specify no version at all” which was bad on nvidia hardware that would go into a nice compatibility mode that would let shaders run on nvidia hardware but break everywhere else. (One of the good/bad things about nvidia having implemented GLSL before there was an actual spec.)

…anyway, I think there are different constants for OpenGL ES versions.

Edit: and I could be totally wrong here about it being a desktop only version. I’m having trouble finding what I want in the source code.

I see… well, so I’ve looked for constants for GLES in the internet and could not find a reliable list. Tried something like GLSL300 (GLSL Versions · mattdesl/lwjgl-basics Wiki · GitHub) and GLSL330 that I’ve seen somewhere else, but it always says that no required hardware can be found.

So I looked at com.jme3.renderer.Caps and only saw OpenGLES20 there that is only one option reminiscent of GLES. I tried it and got com.jme3.renderer.RendererException: This shader cannot run in OpenGL ES 2. Only GLSL 1.00 shaders are supported. so Idk what to make of all this. Probably I will try to find more info in the internet later, when I get to doing shaders, and see what is up. Currently I only need to simply run the app for basic debug, so specifying precision is ok. But it would be cool to get it fixed for the engine in the whole.

Yeah, that’s the only reason I was bugging you about what you tried. :slight_smile:

Incidentally, here is the magic code that sets the #version line:

Starting there but the magic is a little further down.

…so GLSL100 is the one you want for OpenGL ES, I guess. No love here.

Oh really seems so. I don’t know, but that’s a bit weird. Could it be that this code is from the past and that today we have more GLES versions that we can actually choose from? The sources that I have found say that there are multiple GLES versions, so could it be that GLSL100 is not the only option actually?

Just trying to give some more light on this. GL and GLES have different version numbering. As far as I know:

GL ES 2.0 has GLSL ES 100 based on desktop GLSL 120-150 (not sure which one)
GL ES 3.0 has GLSL ES 300 based on desktop GLSL 330

That’s why that comparison of gles2+glsl100 is there

I started trying to add support for GLES 3.0 in my fork but it’s hard and a really big effort is required to properly upgrade shaders to GLSLES300, otherwise there’s nonsense in initializing gl context as 3.0.

A quick reference of porting to 300: http://www.shaderific.com/blog/2014/3/13/tutorial-how-to-update-a-shader-for-opengl-es-30

About the different issues here…

The sampler2DShadow may not be supported on GLESSL100 or depending on the extension, it was supposed to be addressed but the patch to shadows.glsllib was not correct. Se the post at Shadows and post processor filters issues and proposals - #7 by joliver82

The glsllib should be as follows:

#if __VERSION__ >= 130
    // Because gpu_shader5 is actually where those
    // gather functions are declared to work on shadowmaps
    #extension GL_ARB_gpu_shader5 : enable
    #define IVEC2 ivec2
    #if defined HARDWARE_SHADOWS
        #define SHADOWMAP sampler2DShadow
        #define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset)
        #define SHADOWCOMPARE(tex,coord) textureProj(tex, coord)
        #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
    #else
        #define SHADOWMAP sampler2D
        #define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r)
        #define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r)
        #define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
    #endif

    #if FILTER_MODE == 10
        #define GETSHADOW Shadow_Nearest
        #define KERNEL 1.0
    #elif FILTER_MODE == 1
        #ifdef HARDWARE_SHADOWS
            #define GETSHADOW Shadow_Nearest
        #else
            #define GETSHADOW Shadow_DoBilinear_2x2
        #endif
        #define KERNEL 1.0
    #endif
#else
    #define IVEC2 vec2
    #if defined GL_ES
        #define SHADOWMAP sampler2D
        #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
    #elif defined HARDWARE_SHADOWS
        #define SHADOWMAP sampler2DShadow
        #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
    #else
        #define SHADOWMAP sampler2D
        #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
    #endif
...

I would like to get some spare time to get into this topic and finally get shadows to properly work on android (in adition to this quick fix)

About SSAOFilter, I was using SSAO in my project for android also and it worked (slow as hell) so I removed it.

The different precision issue, is weird, reading the code of Lighting.vert and Lighting.frag, there’s no precision specified in the code (at least nowadays at github). Maybe the issue is caused by the GLRenderer setting the precision only for fragment shaders at method updateShaderSourceData?

            if (gles2) {
                // request GLSL ES (1.00) when compiling under GLES2.
                stringBuf.append("#version 100\n");
                
                if (source.getType() == ShaderType.Fragment) {
                    // GLES2 requires precision qualifier.
                    insertPrecision = true;
                }
//... more code here...
        if(insertPrecision){
            // precision token is not a preprocessor dirrective therefore it must be placed after #extension tokens to avoid
            // Error P0001: Extension directive must occur before any non-preprocessor tokens
            int idx = stringBuf.lastIndexOf("#extension");
            idx = stringBuf.indexOf("\n", idx);
            stringBuf.insert(idx + 1, "precision mediump float;\n");
        }

Last but not least, I don’t think using the emulator for testing gl graphical apps is the best way to go. It’s been improved along the years but it’s not the same as a real device.

There’s also some openGLES emulators like qualcomm’s (https://developer.arm.com/tools-and-software/graphics-and-gaming/graphics-development-tools/opengl-es-emulator) that I’ve not used but look good