java - Cuestión extraña de BufferStrategy: el juego se ejecuta rápidamente solo en las GPU Intel
performance (3)
Aquí hay algunas cosas para comprobar:
Sin saber la salida de consola / error de su función setBuffers es difícil decirlo. ¿Intel está utilizando createBufferStrategy (2); mientras que NV usa createBufferStrategy (3); ? Si es así, eso podría ser parte del problema con el uso de memoria adicional.
¿Has probado ya el java2d System.properties? http://docs.oracle.com/javase/1.5.0/docs/guide/2d/flags.html Especialmente la propiedad de seguimiento para la depuración.
Solo Windows
System.setProperty("sun.java2d.transaccel", "True");
System.setProperty("sun.java2d.d3d", "True");
System.setProperty("sun.java2d.ddforcevram", "True");
Todas las plataformas
System.setProperty("sun.java2d.opengl", "True");
Para la depuración
System.setProperty("sun.java2d.trace", "timestamp,log,count");
//// -Dsun.java2d.trace=[log[,timestamp]],[count],[out:<filename>],[help],[verbose]
Toolkit.getDefaultToolkit (). Sync (); en realidad no obliga a monitorear VSync, eso lo hace BufferCapabilities (en su función setBuffers).
BufferStrategy bf = getBufferStrategy();
if (bf != null) {
BufferCapabilities caps = bf.getCapabilities();
try {
Class ebcClass = Class.forName(
"sun.java2d.pipe.hw.ExtendedBufferCapabilities");
Class vstClass = Class.forName(
"sun.java2d.pipe.hw.ExtendedBufferCapabilities$VSyncType");
Constructor ebcConstructor = ebcClass.getConstructor(
new Class[] { BufferCapabilities.class, vstClass });
Object vSyncType = vstClass.getField("VSYNC_ON").get(null);
BufferCapabilities newCaps = (BufferCapabilities)ebcConstructor.newInstance(
new Object[] { caps, vSyncType });
createBufferStrategy(2, newCaps);
// TODO: if success, setCanChangeRefreshRate(false) and setRefreshRate(60).
// Possibly override refreshRateSync()?
}
catch (Throwable t) {
// Ignore
t.printStackTrace();
}
}
EDITE este código fue de ( http://pulpcore.googlecode.com/hg-history/3c4001969922b93048e0a166395115df059a2059/src/pulpcore/platform/applet/BufferStrategySurface.java )
Además, debería llamar a setBuffers DESPUÉS de que el constructor Canvas se haya ejecutado y se haya agregado al JFrame.
Estoy "relativamente seguro" de que no necesitas llamar a super.paint (g); No sé si es lo que está causando su problema específico, pero debe eliminar la línea de su función de pintura.
Aunque no muestres el código, tu mecanismo de explosión con 200 sprites que se mueven aleatoriamente podría ser una causa importante de errores. Calcule la cantidad máxima de explosiones que quiere tener en la pantalla al mismo tiempo y genere esos sprites N * 200 de antemano, póngalos en una lista de arreglos ArrayList (N) y luego en su código haga referencia a ellos cíclicamente. ExplosionClass = explosionList.get (currentExplosionIndex% explosionList.size ());
Me encontré con un problema muy extraño, traté de buscar una respuesta durante días y días. Mi juego acaba de tener un nuevo sistema de partículas, pero era demasiado lento para poder jugarlo. Desafortunadamente, las transformaciones de BufferedImage son muy lentas. El efecto de explosión consiste en unos 200 sprites blancos cargados desde un archivo .png, girados, escalados y coloreados al azar, moviéndose con una velocidad aleatoria.
Intenté mejorar el rendimiento con el almacenamiento en búfer triple / doble y tuve algunos problemas.
Mi primer intento fue con el JPanel en el que se basó el juego. Configuré los buffers en la clase de JFrame (Main), luego realicé el dibujo en la clase Game (extiende JPanel), PERO, sin Gráficos g = bufferstrategy.getDrawGraphics () ;. Luego, al final del método de dibujo, mostré el búfer SI no estaba perdido. El búfer siempre se "perdió", ya que no hice el dibujo con su objeto Graphics. ¡Pero! El juego corre tan rápido como el infierno! Sin búfer en uso práctico! ¿Pero cómo?
Este intento terminó sin errores gráficos y con un gran aumento de rendimiento, pero solo en las tarjetas nVidia / AMD. Intel GPU no pudo manejar esto, la pantalla estaba parpadeando en blanco.
Entonces, terminé configurando y usando BufferStrategy correctamente. La clase Juego ahora extiende Canvas, no JPanel, ya que obtener los Gráficos de un JFrame y usarlos para dibujar en un JPanel termina en un desplazamiento, como se dibuja debajo de la barra de título. Aún rápido, repara 60 FPS.
Ahora, cuando creé BufferStrategy en el JFrame (clase principal), no había ninguna imagen. Corrigí esto configurando BufferStrategy en la clase Game (Canvas). La imagen es correcta ahora, pero el juego en sí es lento como un caracol. Una explosión destruye el FPS hasta ~ 10, pero solo en nVidia / AMD. Irónico. Incluso las antiguas GPU de Intel lo manejan con 60 FPS, logré poner 10000 partículas en movimiento a 60 FPS en una GPU Intel integrada de 5-6 años. Mi tarjeta golpea hasta el 100% de la carga cuando se produce una explosión.
Aquí está mi código principal (el código completo es claro y largo):
public class Game extends Canvas {
-snip-
public void tick() {
BufferStrategy bf = getBufferStrategy();
Graphics g = null;
try {
g = bf.getDrawGraphics();
paint(g);
} finally {
g.dispose();
}
if (!bf.contentsLost()) {
bf.show();
} else {
System.err.println("Buffer lost!");
}
Toolkit.getDefaultToolkit().sync();
}
public void setBuffers() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
if (gc.getBufferCapabilities().isMultiBufferAvailable()) {
createBufferStrategy(3);
System.out.println("Triple buffering active");
} else {
createBufferStrategy(2);
System.err.println("Triple buffering not supported by the GPU");
System.out.println("Double buffering active");
}
System.out.println("FullScreen required: " + getBufferStrategy().getCapabilities().isFullScreenRequired());
System.out.println("Page flipping: " + getBufferStrategy().getCapabilities().isPageFlipping());
}
public void paint(Graphics g) {
super.paint(g);
//set up RenderingHints, draw stuff
}
-snip snip-
}
Por supuesto, llamo a setBuffers () tan pronto como se inicia el Juego.
Espero que pueda ayudarme, este problema es muy importante, ya que usar VolatileImage en mi opinión no dará un aumento en el rendimiento, ya que la manipulación de la imagen debe hacerse usando BufferedImage. Apuesto a que me estoy perdiendo algo trivial, o haciendo algo de manera incorrecta.
Aquí están las especificaciones de mi computadora, solo para demostrar que no es un problema de hardware: Intel Core i7-3770k @ 4.3GHz, nVidia GTX 460, 12 GB de RAM
La computadora "rápida": Intel Core 2 Duo @ 2.7 GHz, Gráficos Intel integrados, 2 GB de RAM
¡Gracias por tu ayuda y tiempo! :)
EDITAR ¿VolatileImage puede ayudar? Si sé bien, la manipulación de la imagen debe hacerse usando BufferedImages, pero dibujarlas es lento.
Cuando envías cosas a la GPU, la GPU lee la información del GDRAM. Si lee los datos en la memoria llamando a getDrawGraphics, entonces la memoria probablemente se lea de la tarjeta gráfica en la memoria RAM. La CPU solo puede acceder a la RAM (DRAM), las GPU solo pueden acceder a la GDRAM. Sin embargo, con las GPU integradas, esto es diferente, ya que no vienen con su propia RAM, usan una parte de la RAM normal.
Para volver a hacer que su código se ejecute rápidamente, determine qué hardware tiene y, en consecuencia, llame al método apropiado (el que está antes del cambio de código o el siguiente).
Verifique el tamaño de las imágenes en comparación con las memorias caché e intente diagnosticar la cantidad de fallas en la memoria caché. Lea sobre diseño orientado a datos, por lo general es una parte de eso.
Una alternativa es cambiar la forma en que haces las explosiones a una manera en que no cargues 200 sprites y los extiendas (¿los reutilizas o los cargas cada vez que haces una explosión?). Hacer una explosión animada requiere menos poder, aunque podría no ser tan espectacular, y entonces necesitarías hacer algo de animación.
Java tampoco es el lenguaje más óptimo para hacer juegos. Estás trabajando a un nivel muy alto, y la JVM ralentiza un poco las cosas. Mis amigos tenían problemas similares cuando hacían juegos pequeños, era difícil hacer un juego que no se retrasara.