Pregunta

Estoy armando un pequeño proyecto para la escuela que implica representar la tabla periódica.Elegí usar LWJGL para hacer esto.Sin embargo, el problema es que cuando renderizo la tabla, el juego comienza a ~30 fps (con un límite de 60 fps) y rápidamente fluctúa a fps de un solo dígito.Creo que el problema podría ser una pérdida de memoria, pero no estoy seguro.¿Alguien puede ver algún problema evidente con mi código?Estas son las clases principales involucradas en la representación de la tabla:

EntidadTablaPeriódica:A cargo de contener una gran variedad de objetos EntityElement (ver más abajo), activando su lógica (tick() y updateInput()).paquete com.flafla2.periodicTable;

import org.lwjgl.opengl.GL11;

public class EntityPeriodicTable extends ClickableEntity { //ClickableEntity is an abstract class in charge of the tick(), updateInput(), and render() methods, as well as positioning

    public EntityElement[] elements = {//This is unfinished, but you get the idea.
        //new EntityElement(Atomic #, State, Metal, "Symbol", "Name", new Vector2D(posx,posy), this)
        new EntityElement(1, 2, 2, "H", "Hydrogen", new Vector2D(1,1), this),
        new EntityElement(2, 2, 2, "He", "Helium", new Vector2D(18,1), this),

        new EntityElement(3, 0, 0, "Li", "Lithium", new Vector2D(1,2), this),
        new EntityElement(4, 0, 0, "Be", "Beryllium", new Vector2D(2,2), this),
        new EntityElement(5, 0, 1, "B", "Boron", new Vector2D(13,2), this),
        new EntityElement(6, 0, 2, "C", "Carbon", new Vector2D(14,2), this),
        new EntityElement(7, 2, 2, "N", "Nitrogen", new Vector2D(15,2), this),
        new EntityElement(8, 2, 2, "O", "Oxygen", new Vector2D(16,2), this),
        new EntityElement(9, 2, 2, "F", "Fluorine", new Vector2D(17,2), this),
        new EntityElement(10,2, 2, "Ne", "Neon", new Vector2D(18,2), this),

        new EntityElement(11, 0, 0, "Na", "Sodium", new Vector2D(1,3), this),
        new EntityElement(12, 0, 0, "Mg", "Magnesium", new Vector2D(2,3), this),
        new EntityElement(13, 0, 0, "Al", "Aluminum", new Vector2D(13,3), this),
        new EntityElement(14, 0, 1, "Si", "Silicon", new Vector2D(14,3), this),
        new EntityElement(15, 0, 2, "P", "Phosphorous", new Vector2D(15,3), this),
        new EntityElement(16, 0, 2, "S", "Sulfur", new Vector2D(16,3), this),
        new EntityElement(17, 2, 2, "Cl", "Chlorine", new Vector2D(17,3), this),
        new EntityElement(18, 2, 2, "Ar", "Argon", new Vector2D(18,3), this),

        new EntityElement(19, 0, 0, "K", "Potassium", new Vector2D(1,4), this),
        new EntityElement(20, 0, 0, "Ca", "Calcium", new Vector2D(2,4), this),
        new EntityElement(21, 0, 0, "Sc", "Scandium", new Vector2D(3,4), this),
        new EntityElement(22, 0, 0, "Ti", "Hydrogen", new Vector2D(4,4), this),
        new EntityElement(23, 0, 0, "V", "Hydrogen", new Vector2D(5,4), this),
        new EntityElement(24, 0, 0, "Cr", "Hydrogen", new Vector2D(6,4), this),
        new EntityElement(25, 0, 0, "Mn", "Hydrogen", new Vector2D(7,4), this),
        new EntityElement(26, 0, 0, "Fe", "Hydrogen", new Vector2D(8,4), this),
        new EntityElement(27, 0, 0, "Co", "Hydrogen", new Vector2D(9,4), this),
        new EntityElement(28, 0, 0, "Ni", "Hydrogen", new Vector2D(10,4), this),
        new EntityElement(29, 0, 0, "Cu", "Hydrogen", new Vector2D(11,4), this),
        new EntityElement(30, 0, 0, "Zn", "Hydrogen", new Vector2D(12,4), this),
        new EntityElement(31, 0, 0, "Ga", "Hydrogen", new Vector2D(13,4), this),
        new EntityElement(32, 0, 1, "Ge", "Hydrogen", new Vector2D(14,4), this),
        new EntityElement(33, 0, 1, "As", "Hydrogen", new Vector2D(15,4), this),
        new EntityElement(34, 0, 2, "Se", "Hydrogen", new Vector2D(16,4), this),
        new EntityElement(35, 1, 2, "Br", "Hydrogen", new Vector2D(17,4), this),
        new EntityElement(36, 2, 2, "Kr", "Hydrogen", new Vector2D(18,4), this),
    };

    public final int ELEMENT_SIZE = 40;
    public Vector2D mousePos = new Vector2D(0,0); //Simple 2D vector struct.

    public double[] SOLID_RGB = {0,0,0};
    public double[] LIQUID_RGB = {0,0,1};
    public double[] GAS_RGB = {1,0,0};

    public double[] METAL_RGB;
    public double[] NONMETAL_RGB;
    public double[] METALLOID_RGB;
    public double[] RECENT_RGB;

    public EntityPeriodicTable(Vector2D pos) {
        this.pos = pos;
        METAL_RGB = new double[3];
        METAL_RGB[0] = 0.596078431; //152/255
        METAL_RGB[1] = 0.984313725; //251/255
        METAL_RGB[2] = 0.596078431; //152/255

        NONMETAL_RGB = new double[3];
        NONMETAL_RGB[0] = 1;
        NONMETAL_RGB[1] = 0.647058824; //165/255
        NONMETAL_RGB[2] = 0;

        METALLOID_RGB = new double[3];
        METALLOID_RGB[0] = 0.866666667; //221/255
        METALLOID_RGB[1] = 0.62745098; //160/255
        METALLOID_RGB[2] = 0.866666667; //221/255

        RECENT_RGB = new double[3];
        RECENT_RGB[0] = 0.803921569; //205/255
        RECENT_RGB[1] = 0.788235294; //201/255
        RECENT_RGB[2] = 0.788235294; //201/255
    }

    @Override
    void render() {
        GL11.glDisable(GL11.GL_TEXTURE_2D);
        GL11.glDisable(GL11.GL_BLEND);
        for(int x=0;x<elements.length;x++)
            elements[x].render();
        GL11.glEnable(GL11.GL_TEXTURE_2D);
        GL11.glEnable(GL11.GL_BLEND);
        for(int x=0;x<elements.length;x++)
            elements[x].renderWithTex();
    }

    @Override
    void tick() {
        for(int x=0;x<elements.length;x++)
            elements[x].tick();
    }

    @Override
    public void updateInput(Vector2D mousePos)
    {
        this.mousePos = mousePos;
        for(int x=0;x<elements.length;x++)
        {
            if(mousePos.isInBoundsWithDim(elements[x].pos.x, elements[x].pos.y, elements[x].dim.x, elements[x].dim.y))
                elements[x].isSelected = true;
            else
                elements[x].isSelected = false;
        }
    }

    @Override
    void onEntityClicked() {
        for(int x=0;x<elements.length;x++)
        {
            if(mousePos.isInBoundsWithDim(elements[x].pos.x, elements[x].pos.y, elements[x].dim.x, elements[x].dim.y))
                elements[x].onEntityClicked();
        }
    }

}

Elemento de entidad:Mantiene datos de un elemento específico en la tabla y los representa (el código de renderizado no está terminado)

package com.flafla2.periodicTable;

import org.lwjgl.opengl.GL11;

public class EntityElement extends ClickableEntity {

    String symbol;
    String element;
    int atomicNumber;
    EntityPeriodicTable table;
    int state;//0=solid, 1=liquid, 2=gas
    int metalState;//0=metal, 1=metalloid, 2=nonmetal, 3=discovered recently
    Vector2D gridPos;

    public EntityElement(int an, int st, int ms, String sy, String en, Vector2D gp, EntityPeriodicTable pt)
    {
        symbol = sy;
        element = en;
        atomicNumber = an;
        table = pt;
        state = st;
        metalState = ms;
        gridPos = gp;

        dim.x = table.ELEMENT_SIZE; dim.y = table.ELEMENT_SIZE;
        pos.x = table.pos.x + table.ELEMENT_SIZE*(gridPos.x-1); pos.y = table.pos.y + table.ELEMENT_SIZE*(gridPos.y-1);
    }

    public double[] getStateColor()
    {
        switch(state)
        {
        case 0:
            return table.SOLID_RGB;
        case 1:
            return table.LIQUID_RGB;
        case 2:
            return table.GAS_RGB;
        default:
            double[] d = {0.0d,0.0d,0.0d};
            return d;
        }
    }

    public double[] getMetalColor()
    {
        switch(metalState)
        {
        case 0:
            return table.METAL_RGB;
        case 1:
            return table.METALLOID_RGB;
        case 2:
            return table.NONMETAL_RGB;
        case 3:
            return table.RECENT_RGB;
        default:
            double[] d = {0.0d,0.0d,0.0d};
            return d;
        }
    }

    @Override
    void render() {
        GL11.glPushMatrix();
            GL11.glTranslatef(pos.x, pos.y, 0);
            double[] d = getMetalColor();
            GL11.glColor3d(d[0], d[1], d[2]);
            GL11.glBegin(GL11.GL_QUADS);
            {
                GL11.glVertex2f(0, 0);//topleft
                GL11.glVertex2f(dim.x, 0);//topright
                GL11.glVertex2f(dim.x, dim.y);//bottomright
                GL11.glVertex2f(0, dim.y);//bottomleft
            }
            GL11.glEnd();
            GL11.glColor3d(1.0d, 1.0d, 1.0d);
        GL11.glPopMatrix();
    }

    public void renderWithTex()
    {
        Font.drawString(symbol, new Vector2D(pos.x+dim.x/2-Font.getStringWidth(symbol,2)/2,pos.y+dim.y/2-Font.FONT_HEIGHT), 2);
    }

    @Override
    void tick() {
        if(isSelected)
        {
            dim.x = table.ELEMENT_SIZE+6; dim.y = table.ELEMENT_SIZE+6;
            pos.x = table.pos.x + table.ELEMENT_SIZE*(gridPos.x-1)-3; pos.y = table.pos.y + table.ELEMENT_SIZE*(gridPos.y-1)-3;
        } else
        {
            dim.x = table.ELEMENT_SIZE; dim.y = table.ELEMENT_SIZE;
            pos.x = table.pos.x + table.ELEMENT_SIZE*(gridPos.x-1); pos.y = table.pos.y + table.ELEMENT_SIZE*(gridPos.y-1);
        }
    }

    @Override
    void onEntityClicked() {

    }

}

Fuente:Maneja la representación de texto en pantalla:

package com.flafla2.periodicTable;

import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;

import org.lwjgl.opengl.GL11;

public class Font {
    public static final String fontText = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:;?!\"&',-.[]#()+ ";
    public static final BufferedImage fontSheet = TextureLoader.loadTexture("/res/text.png");

    public static final int FONT_WIDTH = 9;
    public static final int FONT_HEIGHT = 8;

    public static void drawString(String s, Vector2D pos, float dim)
    {
        drawString(s,pos,new Vector2D((int)Math.floor(dim*FONT_WIDTH),(int)Math.floor(dim*FONT_HEIGHT)));
    }

    public static void drawString(String s, Vector2D pos)
    {
        drawString(s,pos,new Vector2D(9,8));
    }

    public static void drawString(String s, Vector2D pos, Vector2D dim)
    {
        for(int x=0;x<s.length();x++)
        {
            drawLetter(s.charAt(x),new Vector2D(pos.x+dim.x*x,pos.y),dim);
        }
    }

    public static int getStringWidth(String s)
    {
        return s.length()*FONT_WIDTH;
    }

    public static int getStringWidth(String s,float f)
    {
        return (int)Math.floor(s.length()*FONT_WIDTH*f);
    }

    public static Vector2D getPosOfLetterOnImg(Character c,int gridNumb)
    {
        int xOffset = 0;
        int yOffset = 0;
        if(!c.equals(' '))
        {
            int letterNumb = fontText.indexOf(c);
            xOffset = (letterNumb%26)*FONT_WIDTH;
            if(xOffset != 0)
                xOffset -=1;
            yOffset = 0;
            int yGridOffset = (letterNumb < 26) ? 0 : ((letterNumb < 52) ? 1 : 2);

            switch(gridNumb)
            {
            case 1:
                yOffset = 34;
                break;
            case 2:
                yOffset = 69;
                break;
            default:
                yOffset = 0;
            }

            for(int x=0;x<yGridOffset;x++)
                yOffset += FONT_HEIGHT+x+3;
        } else
        {
            xOffset = 235;
            yOffset = 92;
        }

        return new Vector2D(xOffset,yOffset);
    }

    public static void drawLetter(Character c, Vector2D pos, Vector2D dim)
    {
        if(fontSheet == null)
            return;

        Vector2D letterPos = getPosOfLetterOnImg(c,2);

        BufferedImage letterImage = fontSheet.getSubimage(letterPos.x, letterPos.y, FONT_WIDTH, FONT_HEIGHT);
        int textureID = TextureLoader.loadGLTexture(letterImage);
        letterImage = null;

        GL11.glPushMatrix();
            GL11.glTranslatef(pos.x, pos.y, 0);
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
            GL11.glBegin(GL11.GL_QUADS);
            {
                GL11.glTexCoord2f(0, 0);
                GL11.glVertex2f(0, 0);

                GL11.glTexCoord2f(1, 0);
                GL11.glVertex2f(dim.x, 0);

                GL11.glTexCoord2f(1, 1);
                GL11.glVertex2f(dim.x, dim.y);

                GL11.glTexCoord2f(0, 1);
                GL11.glVertex2f(0, dim.y);
            }
            GL11.glEnd();
        GL11.glPopMatrix();
    }
}

Cargador de texturas:Carga texturas (duh jajaja)

package com.flafla2.periodicTable;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteBuffer;

import javax.imageio.ImageIO;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;

public class TextureLoader {
    public static BufferedImage loadTexture(String texturePath)
    {
        try {
            return ImageIO.read(PeriodicTable.class.getResource(texturePath));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    private static final int BYTES_PER_PIXEL = 4;
    public static int loadGLTexture(BufferedImage image){
         int[] pixels = new int[image.getWidth() * image.getHeight()];
         image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());

         ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * BYTES_PER_PIXEL); //4 for RGBA, 3 for RGB

         for(int y = 0; y < image.getHeight(); y++){
             for(int x = 0; x < image.getWidth(); x++){
                 int pixel = pixels[y * image.getWidth() + x];
                 buffer.put((byte) ((pixel >> 16) & 0xFF));     // Red component
                 buffer.put((byte) ((pixel >> 8) & 0xFF));      // Green component
                 buffer.put((byte) (pixel & 0xFF));               // Blue component
                 buffer.put((byte) ((pixel >> 24) & 0xFF));    // Alpha component. Only for RGBA
             } 
         }

         buffer.flip(); //FOR THE LOVE OF GOD DO NOT FORGET THIS

         // You now have a ByteBuffer filled with the color data of each pixel.
         // Now just create a texture ID and bind it. Then you can load it using 
         // whatever OpenGL method you want, for example:

         int textureID = GL11.glGenTextures(); //Generate texture ID
         GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID); //Bind texture ID

         //Setup wrap mode
         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);

         //Setup texture scaling filtering
         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);

         //Send texel data to OpenGL
         GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
         buffer = null;

         //Return the texture ID so we can bind it later again
         return textureID;
    }
}

Lo sé, es mucho código, pero si alguien pudiera ayudarme se lo agradecería mucho.

Gracias, Flafla2.

¿Fue útil?

Solución 2

Muy bien, encontré el problema.

En TextureLoader.java, no usé glDeleteTextures(textureID), por lo que las texturas utilizadas en Font.java no se estaban descargando de la memoria.Ahora, obtengo más de 50 fps estables (en mi macbook de mierda, por supuesto).

Además, la otra respuesta marcada aumentó mis fps a ~60.Por si alguien se lo pregunta, aquí está la nueva drawLetter() método, con cambios:

public static void drawLetter(Character c, Vector2D pos, Vector2D dim)
    {
        if(fontSheet == null)
            return;

        Vector2D letterPos = getPosOfLetterOnImg(c,2);

        //BufferedImage letterImage = fontSheet.getSubimage(letterPos.x, letterPos.y, FONT_WIDTH, FONT_HEIGHT);
        //int textureID = TextureLoader.loadGLTexture(letterImage);
        //letterImage = null;

        int width = fontSheet.getWidth(); int height = fontSheet.getHeight();
        double d[] = {(double)letterPos.x/width, (double)letterPos.y/height, (double)(letterPos.x+FONT_WIDTH)/width, (double)(letterPos.y+FONT_HEIGHT)/height};
        GL11.glPushMatrix();
            GL11.glTranslatef(pos.x, pos.y, 0);
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
            GL11.glBegin(GL11.GL_QUADS);
            {
                GL11.glTexCoord2d(d[0], d[1]);
                GL11.glVertex2f(0, 0);

                GL11.glTexCoord2d(d[2], d[1]);
                GL11.glVertex2f(dim.x, 0);

                GL11.glTexCoord2d(d[2], d[3]);
                GL11.glVertex2f(dim.x, dim.y);

                GL11.glTexCoord2d(d[0], d[3]);
                GL11.glVertex2f(0, dim.y);
            }
            GL11.glEnd();
        GL11.glPopMatrix();

        //GL11.glDeleteTextures(textureID);
    }

Otros consejos

Pensé que todo lo resolvió, hay más espacio para mejorar.Veo que tiene su fuente en una imagen y para cada personaje que desea dibujar, la parte de la imagen con esa letra lo carga en una textura y luego se necesita limpiar.

Mejor cargar la imagen entera en una gran textura, mantenga esa textura durante la duración de su programa y reúnelo cuando se presenta cada cuadro.Puede seleccionar los caracteres correctos para renderizar especificando las coordenadas de textura correctas.

Debe poder presionar su límite de 60FPS con un uso bajo de CPU a menos que su MacBook sea realmente antiguo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top