2012-03-10 156 views
0

我正在为涉及呈现元素周期表的学校组织一个小型项目。我选择使用LWJGL来做到这一点。然而,问题在于,当我渲染桌子时,游戏以30fps开始(以60fps上限),并且很快波动到一位数的fps。我相信问题可能是内存泄漏,但我不确定。任何人都可以看到我的代码有什么明显的问题吗?这里是涉及渲染表的主要类:提升我的LWJGL游戏性能的最佳方法?

EntityPeriodicTable:负责持有大量的EntityElement对象(见下文),激活它们的逻辑(tick()和updateInput())。 package 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(); 
     } 
    } 

} 

EntityElement:保存在表中的特定元件的数据,并对其进行渲染(呈现代码是未完成)

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() { 

    } 

} 

字体:句柄呈现文本屏幕上:

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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz:;?!\"&',-.[]#()+ "; 
    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(); 
    } 
} 

TextureLoader :负载纹理(杜拉尔)

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; 
    } 
} 

我知道,这是很多代码,但如果任何人都可以帮助我,它将不胜感激。

谢谢,Flafla2。

+2

你说内存泄漏,你有没有试过看用法?你'新'了很多载体,这是正常的,但是这将是我看起来的第一个地方。如果可以,请尝试分析CPU和内存使用情况,并查看热点位置。但是请注意,在CPU上分析渲染代码*会提供*不正确的数据,并且应该大部分忽略*(驱动程序/卡缓冲区过大以致CPU配置文件不匹配)。 – ssube 2012-03-10 18:32:38

+0

谢谢,我现在会尝试分析(我以前遇到过分析器问题,我会看看能否解决问题)。 – Flafla2 2012-03-10 18:38:04

+1

好吧,我发现了这个问题。 在'TextureLoader.java'中,我没有使用'glDeleteTextures(textureID)',所以'Font.java'中使用的纹理没有从内存中卸载。现在,我获得了稳定的50+ fps(当然,在我的蹩脚MacBook上)。不过感谢您的帮助! – Flafla2 2012-03-10 19:52:19

回答

1

好吧,我发现了这个问题。

TextureLoader.java,我没有使用glDeleteTextures(textureID),所以Font.java中使用的纹理没有从内存中卸载。现在,我获得了稳定的50+ fps(当然,在我的蹩脚MacBook上)。

此外,其他检查的答案提高我的fps到~60。如果有人想知道,这里是新的drawLetter()方法,有变化:

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); 
    } 
1

认为你已经解决了它有更多的改进空间。我看到你的图像中有你的字体,并且你想要绘制的每个字符都会得到图像的一部分,然后将这些信息加载到纹理中,然后清理所有需要清理的字体。

更好地将整个图像加载到一个大纹理中,在整个程序期间保持该纹理并在渲染每个帧时重用它。您可以通过指定正确的纹理坐标来选择正确的字符进行渲染。

你应该能够以低cpu使用率达到60fps上限,除非你的MacBook真的很旧。

+0

谢谢,这使我的FPS从30〜60! – Flafla2 2012-03-11 13:35:52