试图从这个 jogl 程序中获得更多的 fps。理论fps很高,但实际fps很低。jvisualvm 说大部分(超过 90%)的时间都花在了 AWTAnimatorImpl.display() 和 GLDrawableHelper.displayImpl() 上。
package stanalone;
import static java.awt.Color.cyan;
import static java.awt.Color.magenta;
import static java.awt.Color.white;
import static java.awt.Color.yellow;
import static java.awt.Color.red;
import static java.lang.Math.PI;
import static java.lang.Math.cos;
import static java.lang.Math.min;
import static java.lang.Math.signum;
import static java.lang.Math.sin;
import static javax.media.opengl.GL.GL_ARRAY_BUFFER;
import static javax.media.opengl.GL.GL_COLOR_BUFFER_BIT;
import static javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT;
import static javax.media.opengl.GL.GL_DEPTH_TEST;
import static javax.media.opengl.GL.GL_FLOAT;
import static javax.media.opengl.GL.GL_FRONT;
import static javax.media.opengl.GL.GL_FRONT_AND_BACK;
import static javax.media.opengl.GL.GL_LEQUAL;
import static javax.media.opengl.GL.GL_MAX_TEXTURE_SIZE;
import static javax.media.opengl.GL.GL_NICEST;
import static javax.media.opengl.GL.GL_POINTS;
import static javax.media.opengl.GL.GL_WRITE_ONLY;
import static javax.media.opengl.GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT;
import static javax.media.opengl.GL2GL3.GL_FILL;
import static javax.media.opengl.fixedfunc.GLLightingFunc.GL_SMOOTH;
import static javax.media.opengl.fixedfunc.GLMatrixFunc.GL_MODELVIEW;
import static javax.media.opengl.fixedfunc.GLMatrixFunc.GL_PROJECTION;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLJPanel;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLPointerFunc;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.util.FPSAnimator;
import com.jogamp.opengl.util.gl2.GLUT;
class Histogram {
public Histogram() {
this(10,0,1);
}
public Histogram(int bins,double low,double high) {
this.bins=bins;
bin=new int[bins];
this.low=low;
this.high=high;
range=high-low;
}
public void add(double[] x) {
for(int i=0;i<x.length;i++)
add(x[i]);
}
public void add(double x) {
n++;
sum+=x;
final double x2=x*x;
sum2+=x2;
min=Math.min(min,x);
max=Math.max(max,x);
if(x>=high)
overflows++;
else if(x<low)
underflows++;
else {
double val=x-low;
int index=(int)(bins*(val/range));
bin[index]++;
}
}
public void clear() {
for(int i=0;i<bins;i++)
bin[i]=0;
overflows=0;
underflows=0;
min=Double.MAX_VALUE;
max=Double.MIN_VALUE;
}
public int n() {
return n;
}
public double low() {
return low;
}
public double high() {
return high;
}
public double range() {
return high()-low();
}
public int bins() {
return bins;
}
public double min() {
return n==0?Double.NaN:min;
}
public double max() {
return n==0?Double.NaN:max;
}
public double sum() {
return n==0?Double.NaN:sum;
}
public double mean() {
return n==0?Double.NaN:sum/n;
}
public double variance() {
return n==0?Double.NaN:sum2/n-mean()*mean();
}
public int bin(int index) {
if(index<0)
return underflows;
else if(index>=bins)
return overflows;
else return bin[index];
}
public double maxDifference() {
double max=0;
for(int i=0;i<bins();i++)
max=Math.max(max,Math.abs(bin(i)-n()/(double)bins())/(n()/(double)bins()));
return max;
}
public String toString() {
final StringBuffer sb=new StringBuffer();
sb.append((float)min()).append("<=").append((float)mean()).append("<=").append((float)max()).append(" ");
sb.append(bin(-1)).append(",[");
for(int i=0;i<bins;i++)
sb.append(i>0?",":"").append(bin(i));
sb.append("],").append(bin(bins));
return sb.toString();
}
private int[] bin;
private int n,bins,underflows,overflows;
private final double low,high,range;
private double min=Double.MAX_VALUE,max=Double.MIN_VALUE,sum,sum2;
}
class MyDataObject {
MyDataObject(Point2D[] points) {
this(points,white);
}
MyDataObject(Point2D[] points,Color color) {
this.points=points;
this.color=color;
}
Point2D[] points;
Color color;
static Point2D[] randomPoints(Random random,Point2D offset) {
List<Point2D> l=new LinkedList<Point2D>();
int n=nPoints/pieces.length;
for(int j=0;j<n;j++)
l.add(new Point2D.Double(offset.getX()+random.nextFloat(),offset.getY()+random.nextFloat()));
return l.toArray(new Point2D[0]);
}
static MyDataObject[] pieces;
static Color[] colors=new Color[]{cyan,magenta,yellow,white};
static int nPoints=1000000;
}
class StandAlone implements GLEventListener {
StandAlone(GLAutoDrawable drawable) {
this.drawable=drawable;
drawable.addGLEventListener(this);
init();
}
void init() {
nVbos=4;
vertexBufferIndices=new int[nVbos];
for(int i=0;i<vertexBufferIndices.length;i++)
vertexBufferIndices[i]=-1;
numberOFVertices=new int[nVbos];
// drawAxes=true;
MyDataObject.pieces=new MyDataObject[nVbos];
for(int i=0;i<nVbos;i++) {
Point2D offset=new Point.Double(min(0,signum(cos(PI/4+i*PI/2))),min(0,signum(sin(PI/4+i*PI/2))));
MyDataObject.pieces[i]=new MyDataObject(MyDataObject.randomPoints(random,offset),MyDataObject.colors[i%MyDataObject.colors.length]);
}
}
static void setupFrame(Component component) {
setupFrame(component,defaultFps);
}
static void setupFrame(Component component,int fps) {
component.setPreferredSize(new Dimension(displayWidth,displayHeight));
final FPSAnimator animator=new FPSAnimator((GLAutoDrawable)component,fps,true);
final JFrame frame=new JFrame();
frame.getContentPane().add(component);
frame.addWindowListener(new WindowAdapter() {
@Override public void windowClosing(WindowEvent e) {
new Thread() {
@Override public void run() {
if(animator.isStarted())
animator.stop();
System.exit(0);
}
}.start();
}
});
frame.setTitle(TITLE);
frame.pack();
frame.setVisible(true);
animator.start();
}
static void setup() {
GLProfile glprofile=GLProfile.getDefault();
GLCapabilities glcapabilities=new GLCapabilities(glprofile);
GLJPanel panel=new GLJPanel(glcapabilities);
new StandAlone(panel);
setupFrame(panel,200);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override public void run() {
setup();
}
});
}
private void createVbo(GL2 gl2,int[] n,int index) {
if(!gl2.isFunctionAvailable("glGenBuffers")||!gl2.isFunctionAvailable("glBindBuffer")||!gl2.isFunctionAvailable("glBufferData")||!gl2.isFunctionAvailable("glDeleteBuffers")) { throw new RuntimeException("Vertex buffer objects not supported."); }
gl2.glGenBuffers(1,vertexBufferIndices,index);
// create vertex buffer data store without initial copy
gl2.glBindBuffer(GL_ARRAY_BUFFER,vertexBufferIndices[index]);
gl2.glBufferData(GL_ARRAY_BUFFER,n[0]*3*Buffers.SIZEOF_FLOAT*2,null,GL.GL_DYNAMIC_DRAW);
}
private static void storeVerticesAndColors(FloatBuffer floatbuffer,MyDataObject w) {
for(Point2D p:w.points) {
floatbuffer.put((float)p.getX()).put((float)p.getY()).put(0);
floatbuffer.put((float)(w.color.getRed()/255.));
floatbuffer.put((float)(w.color.getGreen()/255.));
floatbuffer.put((float)(w.color.getBlue()/255.));
}
floatbuffer.rewind();
}
private void fillVertexBuffer(GL2 gl2,MyDataObject piece,int index) {
// map the buffer and write vertex and color data directly into it
gl2.glBindBuffer(GL_ARRAY_BUFFER,vertexBufferIndices[index]);
ByteBuffer bytebuffer=gl2.glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY);
FloatBuffer floatbuffer=bytebuffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
storeVerticesAndColors(floatbuffer,piece);
gl2.glUnmapBuffer(GL_ARRAY_BUFFER);
}
protected int createAndFillVertexBuffer(GL2 gl2,MyDataObject piece,int index) {
int[] n=new int[]{piece.points.length};
if(vertexBufferIndices[index]==-1)
createVbo(gl2,n,index);
fillVertexBuffer(gl2,piece,index);
return n[0];
}
private void renderPiece(GLAutoDrawable drawable,int index) {
final GL2 gl2=drawable.getGL().getGL2();
gl2.glColorMaterial(GL_FRONT_AND_BACK,GLLightingFunc.GL_AMBIENT_AND_DIFFUSE);
gl2.glEnable(GLLightingFunc.GL_COLOR_MATERIAL);
// draw all objects in vertex buffer
gl2.glBindBuffer(GL_ARRAY_BUFFER,vertexBufferIndices[index]);
gl2.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
gl2.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY);
gl2.glVertexPointer(3,GL_FLOAT,6*Buffers.SIZEOF_FLOAT,0);
gl2.glColorPointer(3,GL_FLOAT,6*Buffers.SIZEOF_FLOAT,3*Buffers.SIZEOF_FLOAT);
gl2.glPolygonMode(GL_FRONT,GL_FILL);
gl2.glDrawArrays(GL_POINTS,0,1*numberOFVertices[index]);
// disable arrays once we're done
gl2.glBindBuffer(GL_ARRAY_BUFFER,0);
gl2.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
gl2.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY);
gl2.glDisable(GLLightingFunc.GL_COLOR_MATERIAL);
}
private void startTimeReporting() {
t0=System.nanoTime();
if(frame%reportPeriod==0)
t0Frame=t0;
if(frame>1) {
double dt=(System.nanoTime()-t0Display);
double millis=dt/1000000.;
hDisplay.add(millis);
}
}
void endTimeReporting() {
double dt=(System.nanoTime()-t0);
double millis=dt/1000000.;
hRender.add(millis);
if(++frame%reportPeriod==0) {
System.out.println("average render time: "+hRender.mean()+" ms., max fps="+1000./hRender.mean());
double dtFrames=System.nanoTime()-t0Frame;
System.out.println("average time between calls to display: "+hDisplay.mean()+" ms., actual fps="+reportPeriod/(dtFrames/1000000.)*1000.);
hRender.clear();
}
t0Display=System.nanoTime();
}
void update() {
angle+=1;
}
@Override public void reshape(GLAutoDrawable drawable,int x,int y,int width,int height) {
System.out.println("super.reshape "+drawable);
GL2 gl=drawable.getGL().getGL2();
if(height==0)
height=1;
float aspect=(float)width/height;
gl.glViewport(0,0,width,height);
gl.glMatrixMode(GL_PROJECTION);
gl.glLoadIdentity();
// glu.gluPerspective(45.0,aspect,0.1,100.0);
gl.glMatrixMode(GL_MODELVIEW);
gl.glLoadIdentity();
}
@Override public void init(final GLAutoDrawable drawable) {
GL2 gl=drawable.getGL().getGL2();
glu=new GLU();
glut=new GLUT();
gl.glClearColor(0.0f,0.0f,0.0f,0.0f);
gl.glClearDepth(1.0f);
gl.glEnable(GL_DEPTH_TEST);
gl.glDepthFunc(GL_LEQUAL);
gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
gl.glShadeModel(GL_SMOOTH);
for(int i=0;i<nVbos;i++)
numberOFVertices[i]=createAndFillVertexBuffer(drawable.getGL().getGL2(),MyDataObject.pieces[i],i);
}
@Override public void display(GLAutoDrawable drawable) {
update();
if(doTimeReporting)
startTimeReporting();
GL2 gl=drawable.getGL().getGL2();
gl.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glRotated(angle,0,1,0);
gl.glColor3d(1,1,1);
for(int i=0;i<nVbos;i++)
renderPiece(drawable,i);
if(doTimeReporting)
endTimeReporting();
}
@Override public void dispose(GLAutoDrawable drawable) {}
final GLAutoDrawable drawable;
double angle;
int nVbos;
int[] vertexBufferIndices;
int[] numberOFVertices;
Random random=new Random();
final int reportPeriod=100;
boolean doTimeReporting=true;
long t0,t0Frame,t0Display;
int frame;
Histogram hRender=new Histogram(10,0,10),hDisplay=new Histogram(10,0,100);
int fps=defaultFps;
protected GLU glu;
protected GLUT glut;
protected static String TITLE="JOGL 2.0 Setup (GLJPanel)";
protected static final int displayWidth=1024;
protected static final int displayHeight=1024;
protected static final int defaultFps=60;
}