Android Question libGDX: Error Compiling Shader

Jeffrey Cameron

Well-Known Member
Licensed User
After learning about the pause and resume events I decided to re-create the program using a different approach.

I designed it so the Main program will load the asset manager and create two screens for the screen manager. I then created two classes, one to handle each screen. The Main just acts as a message-pump and forwards the show/resize/etc. events to each class as necessary. Each class is initialized in the Show event for its screen.

At the start of the program, once the asset manager is done loading, I can initially display screen one or screen two with no errors.

If I display screen one initially and then try to switch to screen two, I get an error compiling shader while initializing the screen two class:
B4X:
java.lang.IllegalArgumentException: Error compiling shader:
    at com.badlogic.gdx.graphics.g2d.SpriteBatch.createDefaultShader(SourceFile:157)
    at com.badlogic.gdx.graphics.g2d.SpriteBatch.<init>(SourceFile:120)
    at com.badlogic.gdx.graphics.g2d.SpriteBatch.<init>(SourceFile:77)
    at anywheresoftware.b4a.libgdx.graphics.lgSpriteBatch.Initialize(SourceFile:29)
    at com.badlogic.gdx.scenes.scene2d.Stage.<init>(SourceFile:130)
    at anywheresoftware.b4a.libgdx.scene2d.lgStage.Initialize(SourceFile:38)
    at phx.cards2.clscribscreen._initialize(clscribscreen.java:142)
    at phx.cards2.main._gamescreen_show(main.java:501)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at anywheresoftware.b4a.libgdx.lgScreenManager$lgScreen.show(SourceFile:139)
    at anywheresoftware.b4a.libgdx.lgScreenManager.setCurrentScreen(SourceFile:61)
    at phx.cards2.main._titlescreen_class_play_click(main.java:746)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at anywheresoftware.b4a.keywords.Common$5.run(Common.java:952)
    at android.os.Handler.handleCallback(Handler.java:587)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:130)
    at android.app.ActivityThread.main(ActivityThread.java:3689)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
    at dalvik.system.NativeStart.main(Native Method)
java.lang.IllegalArgumentException: Error compiling shader:
The statement that causes the error is when I try to initialize the main stage for the screen (the stage and all groups/tables for the screen are declared in the class, not passed in); for example: MainStage.Initialize("MainEvent") where MainStage is defined as lgScn2DStage.

I'm sure there is something fundamental that I'm not understanding or thread rule that I'm breaking that I don't know about but for the life of me I can't track it down.

Any suggestions would be most welcome!

Thanks in advance,
- Richard
 

Informatix

Expert
Licensed User
After learning about the pause and resume events I decided to re-create the program using a different approach.

I designed it so the Main program will load the asset manager and create two screens for the screen manager. I then created two classes, one to handle each screen. The Main just acts as a message-pump and forwards the show/resize/etc. events to each class as necessary.
I did the same thing for my current game. I think that's the better code organization for libGDX.

Each class is initialized in the Show event for its screen.

At the start of the program, once the asset manager is done loading, I can initially display screen one or screen two with no errors.

If I display screen one initially and then try to switch to screen two, I get an error compiling shader while initializing the screen two class:
B4X:
java.lang.IllegalArgumentException: Error compiling shader:
    at com.badlogic.gdx.graphics.g2d.SpriteBatch.createDefaultShader(SourceFile:157)
    at com.badlogic.gdx.graphics.g2d.SpriteBatch.<init>(SourceFile:120)
    at com.badlogic.gdx.graphics.g2d.SpriteBatch.<init>(SourceFile:77)
    at anywheresoftware.b4a.libgdx.graphics.lgSpriteBatch.Initialize(SourceFile:29)
    at com.badlogic.gdx.scenes.scene2d.Stage.<init>(SourceFile:130)
    at anywheresoftware.b4a.libgdx.scene2d.lgStage.Initialize(SourceFile:38)
    at phx.cards2.clscribscreen._initialize(clscribscreen.java:142)
    at phx.cards2.main._gamescreen_show(main.java:501)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at anywheresoftware.b4a.libgdx.lgScreenManager$lgScreen.show(SourceFile:139)
    at anywheresoftware.b4a.libgdx.lgScreenManager.setCurrentScreen(SourceFile:61)
    at phx.cards2.main._titlescreen_class_play_click(main.java:746)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at anywheresoftware.b4a.keywords.Common$5.run(Common.java:952)
    at android.os.Handler.handleCallback(Handler.java:587)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:130)
    at android.app.ActivityThread.main(ActivityThread.java:3689)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
    at dalvik.system.NativeStart.main(Native Method)
java.lang.IllegalArgumentException: Error compiling shader:
The statement that causes the error is when I try to initialize the main stage for the screen (the stage and all groups/tables for the screen are declared in the class, not passed in); for example: MainStage.Initialize("MainEvent") where MainStage is defined as lgScn2DStage.

I'm sure there is something fundamental that I'm not understanding or thread rule that I'm breaking that I don't know about but for the life of me I can't track it down.

Any suggestions would be most welcome!

Thanks in advance,
- Richard
Are you sure that you have only one stage and that you initialize it only once? Your Stage should be initialized in the Create event of libGDX, not in the screen classes. At the beginning of your screen code, in Show, call the Clear function of Stage if you want to display different actors from the previous screen.
 

Jeffrey Cameron

Well-Known Member
Licensed User
Each class creates and manages it's own stage, for example, in the "Class_Globals" of the class module:
B4X:
Private moMainStage As lgScn2DStage     ' main stage that holds everything for this screen
Private moTitleBackTex As lgTexture     ' texture to hold the title screen backgroun (moMainTB.Background)
Private moMainTB As lgScn2DTable     ' main table that fills the main stage, it's background is the title screen
Then the class' Initialize event creates the stage:
B4X:
Public Sub Initialize(oCaller As Object, sBaseEvent As String)
    moCaller = oCaller
    msBaseEvent = sBaseEvent
 
    ' title screen background
    If moTitleBackTex.IsInitialized = False Then
         moTitleBackTex.InitializeWithPixmap(Main.AssetMgr.Get("title_screen.png"))
    End If
 
    ' main game stage
    If moMainStage.IsInitialized = False Then
        moMainStage.Initialize("TitleStage")
        SetupScreen  'creates the various widgets and fills the main stage
    End If
End Sub
Should the stage variables be created in the Main in the root render thread as opposed to being created in each class?
 

Informatix

Expert
Licensed User
Each class creates and manages it's own stage, for example, in the "Class_Globals" of the class module:
B4X:
Private moMainStage As lgScn2DStage     ' main stage that holds everything for this screen
Private moTitleBackTex As lgTexture     ' texture to hold the title screen backgroun (moMainTB.Background)
Private moMainTB As lgScn2DTable     ' main table that fills the main stage, it's background is the title screen
Then the class' Initialize event creates the stage:
B4X:
Public Sub Initialize(oCaller As Object, sBaseEvent As String)
    moCaller = oCaller
    msBaseEvent = sBaseEvent

    ' title screen background
    If moTitleBackTex.IsInitialized = False Then
         moTitleBackTex.InitializeWithPixmap(Main.AssetMgr.Get("title_screen.png"))
    End If

    ' main game stage
    If moMainStage.IsInitialized = False Then
        moMainStage.Initialize("TitleStage")
        SetupScreen  'creates the various widgets and fills the main stage
    End If
End Sub
Should the stage variables be created in the Main in the root render thread as opposed to being created in each class?
If you want to create multiple stages, you have to initialize them with Initialize3 and manage yourself the SpriteBatch they use. But honestly, I can't see the benefit of having multiple stages.
 

Jeffrey Cameron

Well-Known Member
Licensed User
I created multiple stages because I was having an issue re-using the stage between screens. If I create just one stage and pass it to each class (using the stage's .Clear method before I start filling in the stage) the second screen stage does not render properly, the background being displayed is the same background from the first screen even though this background was disposed during the pause event of the first screen.

Even stranger, if I create two separate stages and pass one to the first class and another to the second class, the button widgets on the first stage no longer respond to touch events (the _Clicked event in the class no longer fires and the button doesn't switch between the "up" and "down" images).

Each class initializes a lgScn2dGroup that gets added to the stage and contains all other actors and widgets. Does the group object I'm creating need to be declared in the Main as well and not in each class?
 

Informatix

Expert
Licensed User
I created multiple stages because I was having an issue re-using the stage between screens. If I create just one stage and pass it to each class (using the stage's .Clear method before I start filling in the stage) the second screen stage does not render properly, the background being displayed is the same background from the first screen even though this background was disposed during the pause event of the first screen.
Although all screens in my game have a different background and use a different set of actors in the same stage, I never saw the problem that you mention so I think that you made a mistake somewhere.

Even stranger, if I create two separate stages and pass one to the first class and another to the second class, the button widgets on the first stage no longer respond to touch events (the _Clicked event in the class no longer fires and the button doesn't switch between the "up" and "down" images).
This is normal. There's only one input processor and gesture detector for all scenes. So when you initialize one, all others lose the ability to receive user events.

Each class initializes a lgScn2dGroup that gets added to the stage and contains all other actors and widgets. Does the group object I'm creating need to be declared in the Main as well and not in each class?
It has to be declared in the Globals section of the class so it is available for all class functions (and other classes if it is public). Why do you want to declare it in Main?
 

Jeffrey Cameron

Well-Known Member
Licensed User
... Why do you want to declare it in Main?
I didn't want to, that kind of defeats the purpose of the class, I was just querying to see if I needed to do it that way. I am still working on grasping the underlying mechanics of the library and am not sure which items need/can be declared where.

Do you draw your backgrounds in the render event, or are you using the background property? I create a lgScn2DTextureRegionDrawable from a texture (.Initialize4) that I then assign to the .Background property of a lgScn2dTable that is put inside the group (the table fills the whole group). I did it this way as the group object itself has no background property that I can find. The texture is created and disposed of in the class.

Perhaps I should just draw the texture in the render event and not use the background property?
 

Informatix

Expert
Licensed User
Do you draw your backgrounds in the render event, or are you using the background property?
I use an actor (lgScn2DImage) and it is drawn automatically by Stage.Draw in the Render event. But there are other solutions. E.g. you can draw the texture directly in the Draw event of your stage.

I create a lgScn2DTextureRegionDrawable from a texture (.Initialize4) that I then assign to the .Background property of a lgScn2dTable that is put inside the group (the table fills the whole group). I did it this way as the group object itself has no background property that I can find. The texture is created and disposed of in the class.
What's the usefulness of the group if it contains only a table that fills it ?
 

Jeffrey Cameron

Well-Known Member
Licensed User
What's the usefulness of the group if it contains only a table that fills it ?
The group contains everything for that screen; other actors, other tables, etc. I just happen to use that table's .Background property to provide the background image since the background image is static and unchanging for the life of the screen.
 

Informatix

Expert
Licensed User
The group contains everything for that screen; other actors, other tables, etc. I just happen to use that table's .Background property to provide the background image since the background image is static and unchanging for the life of the screen.
The stage has already a group (root) to contain everything so it seems that your group is not necessary.
 

Jeffrey Cameron

Well-Known Member
Licensed User
Yes, it's an ordinary group.
Good to know, I'll bear that in mind for my next project.

I have changed it from using the .Background property to just displaying a lgScn2DImage and it still renders the first screen's background when you show the second screen. Basically, whatever screen is displayed second shows the background from the initially displayed screen (even though the texture was disposed in the hide event of the prior screen) instead of displaying the image that is created in the class.

The stage is .Clear'ed before I pass it to the class and the textures I'm using are created in the class initialize, disposed in the pause and re-created in the resume event. I'm at a loss to explain this particular behavior.
 

Informatix

Expert
Licensed User
Good to know, I'll bear that in mind for my next project.

I have changed it from using the .Background property to just displaying a lgScn2DImage and it still renders the first screen's background when you show the second screen. Basically, whatever screen is displayed second shows the background from the initially displayed screen (even though the texture was disposed in the hide event of the prior screen) instead of displaying the image that is created in the class.

The stage is .Clear'ed before I pass it to the class and the textures I'm using are created in the class initialize, disposed in the pause and re-created in the resume event. I'm at a loss to explain this particular behavior.
It's difficult to say where's your mistake without the code. If you don't want to make it public, send it to me in private.
 

Jeffrey Cameron

Well-Known Member
Licensed User
I started a conversation with a download link, the project is too large to post to the forum due to the graphics.

Thank you so very much!

- Richard

Edited 12/8/2014 - 9:13am Solution:
If anyone was following this thread looking for a solution, the one I eventually came up with (thanks to Informatix) was to keep all of my images in an atlas and create the backgrounds from the atlas and not load from disk.
 
Last edited:

wonder

Expert
Licensed User
Hey guys!

Say hello to the newest member of the Error Compiling Shader Club! o_O
I'm still trying to figure out if I can manage to fix this without drastically changing the structure of my current code (implementing a Texture Atlas).

Wish me luck... :confused:
 
Last edited:

wonder

Expert
Licensed User
Alright, I've figured it out. :cool:

It seems that the screen change has to occur at the end of the Render event, at least in my case.

Before, I had the change screen instruction being called from a button click:
B4X:
Sub Button1_Click
    LGSM.CurrentScreen = LG_Screen(1)
End Sub
All I had to do was to create a request variable in Process Globals and have the screen changed from the Render event:
B4X:
Sub Process_Globals
    ...
    Dim ScreenChangeRequested = False as Boolean
    ...
End Sub

Sub Button1_Click
    ScreenChangeRequested = True
End Sub

Sub LGS0_Render(Delta As Float)
    ...
    ...
    ...
    ...
    If ScreenChangeRequested Then
        ScreenChangeRequested = False
        LGSM.CurrentScreen = LG_Screen(1)
    End If
End Sub
I hope this helps whoever faces the same challenge! :)
 
Top