Синхронизировано с GLThread в Android

1

Я делаю игру в android, используя opengl-es, используя несколько потоков:

class World{

    protected static final AtomicInteger entityLock = new AtomicInteger();

    private GameEntity entities[];

    public World(){
        // populate game world with entities
        // executed on main thread
        addEntity(new GameEntity("tank"));
        addEntity(new GameEntity("rifleman"));
        addEntity(new GameEntity("rifleman"));
    }

    void update(){
        synchronized(entityLock){
           for(int i = 0;i<entities.length;i++){
                // move entity to new position
                // executed on PhysThread
                entities[i].updatePosition();                    
            }
        }
        if(entity.isDead(){
            // remove entity. Enter sync block inside removeEntity() method
            removeEntity(entity);                  
        }                      
    }

    void draw(GL10 gl){
        synchronized(entityLock){
            for(int i = 0;i<entites.length;i++){
                // draw models
                // executed on GLThread
                Vector3 entityPosition = entities[i].getPosition();
                gl.glTranslatef(entityPosition.x, entityPosition.y, entityPosition.z);
                entities[i].draw();
            }
        }
    }

    public void addEntity(GameEntity entity){
        synchronized(entityLock){
            // arrays stuff
        }
    }

    public void removeEntity(GameEntity entity){
        synchronized(entityLock){
            // arrays stuff
        }
    }

} 

class MyRenderer implements GLSurfaceView.Renderer{

    World world;

    public MyRenderer(World world){
        this.world = world;
    }


    public void onDrawFrame(GL10 gl) {
        // executed on GLThread
        world.draw(gl);             
    }


}

class PhysThreadRunnable implements Runnable{

    private long tickRate = 30;

    private World world;

    private PhysThreadRunnable(World world){
        this.world = world;
    }

    protected void setTickRate(long tickRate){
        this.tickRate = tickRate;
    }

    public void run() {
        while(true){                
            try {
                // executed on PhysThread
                world.update();
                Thread.sleep(1000/tickRate);
            } catch (InterruptedException e) {
                return;
            }

        }
    }
}

MyActivity extends Activity{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        World world = new World(); 
        // sets up the game world, populates it with entities

        // set up GLSurfaceView (simplified)
        setContentView(R.layout.main);
        GLSurfaceView mGLView = findViewById(R.id.myGLSurfaceView);
        mGLView.setRenderer(new MyRenderer(world));

        // start phys thread
        PhysThreadRunnable physThreadRunnable = new PhysThreadRunnable(world);
        Thread physThread = new Thread(physThreadRunnable);
        physThread.start();
    }
}

У меня есть проблема, когда иногда (но не каждый раз), когда я запускаю игру, PhysThread застревает в ожидании блокировки, которая будет выпущена (т.е. когда я иду отлаживать и приостанавливать поток, он просто сидит на synchronized(entityLock) внутри update()

Что действительно странно, что через некоторое время (между 2 секундами и минутой) PhysThread будет разблокирован, и игра будет продолжена, если ни один из потоков не будет заблокирован для более чем нескольких итераций циклов потока. (т.е. игра работает нормально)

Изменить: Я добавил в пример некоторые дополнительные материалы, на случай, если это является причиной проблемы. В основном, обновление и рисование массива объектов, а не одного объекта

  • 0
    Вместо этого используйте LibGDX. Он уже интегрируется с openGL и box2d. Это также довольно низкий уровень. Проверьте это и посмотрите, сколько времени я вас спасла.
  • 0
    Ну, код выше это просто пример кода. Я уже потратил около года на изготовление двигателя. Так что начинать сначала не на картах. Я просто хочу знать, является ли это общая проблема синхронизации или что-то специфическое для GLSurfaceView Android
Теги:
multithreading
opengl-es
synchronized

2 ответа

0
Лучший ответ

В конце концов, я пошел на обманное решение. Я поместил блоки синхронизации вокруг доступа к массиву сущностей и поместил петли for внутри try/catch с помощью ArrayIndexOutOfBounds:

void update(){
    try{
        for(int i = 0;i<entities.length;i++){               
            GameEntity entity = entities[i];                
            synchrnonized(entity){
                entity.updatePosition();
            }               
        }
    }catch(ArrayIndexOutOfBoundsException aioob){
        if(tryAgain){
            update();
        } else {
            return;
        }
    }
}

Проблема с этим решением состоит в том, что если entity.updateposition() выбрасывает ArrayIndexOutOfBoundsException из чего-то совершенно не связанного, то я поймаю его и неверно истолковал бы. Плюс все это немного беспорядочно, и раз в каждый момент кадр или обновление пропускаются

Как мне кажется, проблема решена, я подозреваю, что исходная причина, вероятно, лежит где-то глубже в моем коде, между входом в циклы for и фактическим изменением сущностей, и я действительно не думаю, что было бы справедливо сбрасывать мои весь код здесь.

Я оставлю вопрос без ответа на пару дней, если у кого-нибудь будет лучшее решение

2

Я думаю, что проблема здесь, вероятно, в том, что "синхронизированный" блок не гарантирует справедливости.

Поток OpenGL всегда будет отображаться непрерывно, поэтому он попытается повторно войти в onDraw, как только он закончит его. Поскольку выбор того, какой поток разрешен для входа в синхронизированный блок, является произвольным, возможно, что поток OpenGL пытается восстановить блокировку до того, как она будет выпущена в физический поток, и на основании некоторых произвольных критериев ей предоставляется блокировка и далее, не допуская ввода физического потока.

Это может объяснить, почему это происходит когда-то, а не другие, поскольку это произвольное решение.

Вы можете попробовать реализовать справедливую блокировку вместо блока синхронизации или сделать ее такой, чтобы OpenGL не пыталась перерисовать сцену более одного раза с момента последнего обновления физики (поместите поток визуализации в режим сна до тех пор, пока не произойдет обновление).

  • 0
    Я не думаю, что это правильно. 1. Ожидание синхронизации (физический поток) получит блокировку до завершения ondraw и 2. Потребуется некоторое время, чтобы поток gl снова вызвал ondrawframe, потому что он занят рендерингом (это происходит после выхода ondrawframe).
  • 0
    Я мог спать с GLThread, пока обновление физики не закончилось. Но тогда я с тем же успехом мог бы сложить все в одну ветку. Разные вещи будут обновляться в разное время в игре. Например, я мог бы представить третий поток, который обновляет фабрики в игре, чтобы производить и потреблять раз в секунду, и придавать ему более низкий приоритет. Или когда игра приостанавливается, было бы просто приостановить физический поток, но оставьте GLThread включенным, чтобы игроки могли по-прежнему перемещать камеру, масштабировать и т. Д.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню