【问题标题】:Asynchronous Multithreading: Error compiling shader. (libgdx)异步多线程:编译着色器时出错。 (libgdx)
【发布时间】:2017-05-08 00:37:42
【问题描述】:

我正在使用适用于 Android 的 libgdx api 编写应用程序,但遇到了有关更改状态和多线程的问题。


我最初想要的是两个异步线程同时运行,一个是游戏render线程,另一个是游戏logic线程。然后我希望能够改变游戏的状态,这会改变它们各自对象的更新/渲染方法。显然,渲染线程每帧都在运行(这已经由 libgdx 处理)。这是我尝试这样做的代码建模:

public final class StateManager {

private static State gameState = State.START;

private static StateObject stateObject = new StartScreen();

private static final Object syncObjectUpdate = new Object();
private static final Object syncObjectRender = new Object();

public static void update(float elapsedTime){

    if (!stateObject.isCreated){

        //pause this thread until render thread stops
        synchronized(syncObjectUpdate) {
            try {
                // Calling wait() will block this thread until another thread
                // calls notify() on the object.
                syncObjectUpdate.wait();
            } catch (InterruptedException ignored) {} // Happens if someone interrupts your thread.
        }

        //once the render thread stops-

        stateObject.dispose();

        switch (gameState){

            //note: creating a new object here sets isCreated to true,
            //so the if statements gets called only once per state change.
            case START:
                stateObject = new StartScreen();
                break;
            case GAME:
                stateObject = new GameScreen();
                break;
            case LOADING:
                //stateObject = new LoadingScreen();
                break;
            default:
                System.out.print("ERROR IN STATE!");
                break;
        }//end switch


        //-change the state and resume render thread
        //what if this happens before wait? surround in while loop?
        synchronized(syncObjectRender) {
            syncObjectRender.notify();
        }


    }//end if

    stateObject.update(elapsedTime);
}

public static void render(){
    stateObject.render();

    //if update thread is waiting
    if (MyGdxGame.logicThread.getState() == Thread.State.WAITING){

        //then resume that one,
        synchronized(syncObjectUpdate) {
            syncObjectUpdate.notify();
        }

        // and pause this one
        synchronized(syncObjectRender) {
            try {
                syncObjectRender.wait();
            } catch (InterruptedException ignored) {}
        }

    }//end if

}//end render

public static void changeState(State state){
    stateObject.isCreated = false;
    gameState = state;
}

public static void dispose(){
    stateObject.dispose();
}

public static void resize(int width, int height){
    stateObject.resize(width, height);
}

public static void pause(){
    stateObject.pause();
}

public static void resume(){
    stateObject.resume();
}

}//class

这运行良好,直到我调用 changeState(),它抛出错误:

E/AndroidRuntime(19094): java.lang.IllegalArgumentException: Error compiling shader
  • 认为这是因为 stateObject.render() 在没有 StateObject 存在的情况下被调用(这会更改 spritebatch 等)。

[顺便说一句,如果还不清楚,StateObject 类似于开始屏幕或游戏屏幕,其中有一个由两个不同线程调用的 render() 和 update() 方法。还有,我之前写过这个程序,没有多线程,状态变化机制也很完美。]


这是我的思考过程:

1. 当我更改状态时,更新线程会收到通知。这会暂停更新线程。 (渲染仍在运行)

2. 当渲染线程看到更新线程被暂停时,它会恢复更新线程并暂停渲染线程。这样 stateObject.render() 方法就不会在尝试对已处理的对象调用 render() 之前被调用。

3. 此时,更新线程被通知重新启动,因此它会释放 StateObject 并创建一个新的。

4. 既然已经初始化了一个新的状态对象,stateObject.render() 应该没有问题了。然后它恢复渲染线程。两个线程应该都在运行,宇宙中的和平就会恢复。

现在我的实现或那个逻辑显然有问题,因为我遇到了那个错误。那么……为什么?

非常感谢任何帮助。如果有不清楚的地方,请在 cmets 中告诉我。谢谢!

【问题讨论】:

  • 这个问题没有得到太多的关注,我怀疑我难倒了社区,所以有什么建议可以让这个问题变得更好吗?

标签: java android multithreading libgdx


【解决方案1】:

经过数小时的试验,我能够通过将 switch-case 放入渲染方法并删除行 stateObject.dispose(); 来解决这个问题。就是这样,无需等待线程和锁定对象。现在,这会为状态调用适当的渲染方法,而不会出现更改 spritebatch 的问题(因为它在同一个 OpenGL 线程中)。删除 dispose 方法意味着每当我尝试更改状态时,update 方法总是有要更新的东西。

这是一种变通方法,因为更改状态现在已交给渲染方法,但对性能的影响非常很小。

以下是更改后的工作渲染和更新方法(同一类):

public static void update(float elapsedTime){
    stateObject.update(elapsedTime);
}

public static void render(){
    stateObject.render();

    if (!stateObject.isCreated){

        switch (gameState){

            case START:
                stateObject = new StartScreen();
                break;
            case GAME:
                stateObject = new GameScreen();
                break;
            case LOADING:
                //stateObject = new LoadingScreen();
                break;
            default:
                System.out.print("ERROR IN STATE!");
                break;
        }//end switch

    }//end if

}//end render

我希望这对某人有帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多