Эмулятор CHIP8, написанный на java/libgdx, работает слишком медленно

У меня действительно большая проблема с libgdx. Я написал небольшой эмулятор CHIP8, чтобы узнать, как использовать libgdx, но я все еще пытаюсь понять, как заставить его работать быстрее. Я использую Pixmap для обновления экрана и затем рендеринга его в текстуру, но это, кажется, не лучшее решение, потому что он работает очень медленно. Вот код:

package com.eud0x.chip8gdx;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.TextureData;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class Chip8 extends ApplicationAdapter implements InputProcessor {
    SpriteBatch batch;
    Pixmap screen;
    Texture surface;
    C8Emulator chip8;

    @Override
    public void create () {
        batch = new SpriteBatch();
        chip8 = new C8Emulator();
        screen = new Pixmap(512, 256, Format.RGB888);
        chip8.initEmulator();
        try {
            chip8.loadGame("/home/antonio/PONG");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        surface = new Texture(screen);
        Gdx.input.setInputProcessor(this);
    }

    @Override
    public void render () {
        chip8.emulateCycle();
        if ( chip8.drawflag != 0 ) {
            updateTexture();
            chip8.drawflag = 0;
        }
        batch.begin();
        batch.draw(surface, 0, 0);
        batch.end();
    }
    @Override
    public void dispose() {
        batch.dispose();
        screen.dispose();
        surface.dispose();
    }
    public void updateTexture() {
        screen.setColor(0,0,0,0);
        screen.fill();
        for ( int y = 0; y < 32; y++) {
            for ( int x = 0; x < 64; x++ ) {
                if (chip8.display[y][x] != 0) {
                    for (int cx = x*8; cx < x*8 + 8; cx++) {
                        for ( int cy = y*8; cy < y*8 + 8; cy++) {
                            screen.drawPixel(cx, cy, Color.WHITE.toIntBits());
                        }
                    }
                } 
            }
        }
        surface.draw(screen, 0, 0);
    }
@Override
    public boolean keyDown(int keycode) {
        switch (keycode) {
        case Keys.NUM_1:
            chip8.keyDown(0x1);
            break;
        case Keys.NUM_2:
            chip8.keyDown(0x2);
            break;
        case Keys.NUM_3:
            chip8.keyDown(0x3);
            break;
        case Keys.NUM_4:
            chip8.keyDown(0xC);
            break;
        case Keys.Q:
            chip8.keyDown(0x4);
            break;
        case Keys.W:
            chip8.keyDown(0x5);
            break;
        case Keys.E:
            chip8.keyDown(0x6);
            break;
        case Keys.R:
            chip8.keyDown(0xD);
            break;
        case Keys.A:
            chip8.keyDown(0x7);
            break;
        case Keys.S:
            chip8.keyDown(0x8);
            break;
        case Keys.D:
            chip8.keyDown(0x9);
            break;
        case Keys.F:
            chip8.keyDown(0xE);
            break;
        case Keys.Z:
            chip8.keyDown(0xA);
            break;
        case Keys.X:
            chip8.keyDown(0x0);
            break;
        case Keys.C:
            chip8.keyDown(0xB);
            break;
        case Keys.V:
            chip8.keyDown(0xF);
            break;
        default:
            break;  
        }
        return true;
    }

    @Override
    public boolean keyUp(int keycode) {
        switch (keycode) {
        case Keys.NUM_1:
            chip8.keyUp(0x1);
            break;
        case Keys.NUM_2:
            chip8.keyUp(0x2);
            break;
        case Keys.NUM_3:
            chip8.keyUp(0x3);
            break;
        case Keys.NUM_4:
            chip8.keyUp(0xC);
            break;
        case Keys.Q:
            chip8.keyUp(0x4);
            break;
        case Keys.W:
            chip8.keyUp(0x5);
            break;
        case Keys.E:
            chip8.keyUp(0x6);
            break;
        case Keys.R:
            chip8.keyUp(0xD);
            break;
        case Keys.A:
            chip8.keyUp(0x7);
            break;
        case Keys.S:
            chip8.keyUp(0x8);
            break;
        case Keys.D:
            chip8.keyUp(0x9);
            break;
        case Keys.F:
            chip8.keyUp(0xE);
            break;
        case Keys.Z:
            chip8.keyUp(0xA);
            break;
        case Keys.X:
            chip8.keyUp(0x0);
            break;
        case Keys.C:
            chip8.keyUp(0xB);
            break;
        case Keys.V:
            chip8.keyUp(0xF);
            break;
        default:
            break;  
        }
        return true;
    }

    @Override
    public boolean keyTyped(char character) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        // TODO Auto-generated method stub
        return false;
    }
}

Кто-нибудь знает, как сделать это быстрее?

1 ответ

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

SpriteBatch batch;
Texture pixelSprite;
C8Emulator chip8;

@Override
public void create () {
    batch = new SpriteBatch();
    chip8 = new C8Emulator();

    Pixmap textureSource = new Pixmap(1, 1, Format.RGB8888);
    textureSource.setColor(1, 1, 1, 1);
    textureSource.fill();
    pixelSprite = new Texture(textureSource);
    textureSource.dispose();

    chip8.initEmulator();
    try {
        chip8.loadGame("/home/antonio/PONG");
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    Gdx.input.setInputProcessor(this);
}

@Override
public void render () {
    chip8.emulateCycle();

    //don't use draw flags. OpenGL redraws everything every frame
    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    batch.begin();
    for ( int y = 0; y < 32; y++) {
        for ( int x = 0; x < 64; x++ ) {
            if (chip8.display[y][x] != 0) {
                spriteBatch.draw(pixelSprite, x*8, y*8, 8, 8);
            } 
        }
    }
    batch.end();
}

Если вы хотите действительно оптимизировать его, вы можете напрямую манипулировать объектом Mesh для рисования точек вместо спрайтов. Это уменьшит размер данных вершин спрайтов примерно на 80-90%, так что рисование чисто белого экрана будет очень быстрым даже на старых телефонах. Но это, вероятно, не обязательно. Вам, вероятно, вместо этого нужно будет ограничить симуляцию, чтобы замедлить ее.

Другие вопросы по тегам