2009-12-18 20 views
7

Estoy notando una gran diferencia de rendimiento entre Java & JOGL y C# & Tao.OpenGL cuando ambos cargan PNG desde el almacenamiento en la memoria y al cargar esa imagen Buffered (java) o mapa de bits (C# - ambos son PNG en el disco duro) 'en' OpenGL.Cargando PNG en problemas de rendimiento OpenGL - Java y JOGL mucho más lento que C# & Tao.OpenGL

Esta diferencia es bastante grande, así que supuse que estaba haciendo algo mal, sin embargo después de bastante buscar e intentar con diferentes técnicas de carga, no he podido reducir esta diferencia.

Con Java obtengo una imagen cargada en 248ms y cargada en OpenGL en 728ms Lo mismo en C# toma 54ms para cargar la imagen, y 34ms para cargar/crear textura.

La imagen en cuestión es una PNG que contiene transparencias, con un tamaño de 7200x255, utilizada para un sprite animado en 2D. Me doy cuenta de que el tamaño es realmente bastante ridículo y estoy considerando cortar el sprite, sin embargo, la gran diferencia sigue ahí (y confusa).

En el lado de Java el código es el siguiente:

BufferedImage image = ImageIO.read(new File(fileName)); 
texture = TextureIO.newTexture(image, false); 
texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); 
texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); 

El código C# utiliza:

Bitmap t = new Bitmap(fileName); 

t.RotateFlip(RotateFlipType.RotateNoneFlipY); 
Rectangle r = new Rectangle(0, 0, t.Width, t.Height); 

BitmapData bd = t.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 

Gl.glBindTexture(Gl.GL_TEXTURE_2D, tID); 
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, t.Width, t.Height, 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, bd.Scan0); 
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR); 
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); 

t.UnlockBits(bd); 
t.Dispose(); 

Después de un buen montón de pruebas que sólo se puede llegar a la conclusión de que Java/JOGL es más lento aquí - la lectura de PNG puede no ser tan rápida o que todavía estoy haciendo algo mal.

Gracias.

Edit2:

he encontrado que la creación de un nuevo BufferedImage con formato TYPE_INT_ARGB_PRE disminuye el tiempo de carga de OpenGL textura casi a la mitad - esto incluye tener que crear el nuevo BufferedImage, conseguir la Graphics2D de ella y luego hacer que la ya cargada imagen a ella.

Edit3: Resultados de puntos de referencia para 5 variaciones. Escribí una pequeña herramienta de evaluación comparativa, los siguientes resultados provienen de cargar un conjunto de 33 pngs, la mayoría son muy anchos, 5 veces.

testStart: ImageIO.read(file) -> TextureIO.newTexture(image) 
result: avg = 10250ms, total = 51251 
testStart: ImageIO.read(bis) -> TextureIO.newTexture(image) 
result: avg = 10029ms, total = 50147 
testStart: ImageIO.read(file) -> TextureIO.newTexture(argbImage) 
result: avg = 5343ms, total = 26717 
testStart: ImageIO.read(bis) -> TextureIO.newTexture(argbImage) 
result: avg = 5534ms, total = 27673 
testStart: TextureIO.newTexture(file) 
result: avg = 10395ms, total = 51979 

ImageIO.read (bis) se refiere a la técnica descrita en la respuesta de James Branigan a continuación. argbImage se refiere a la técnica descrita en mi anterior edición:

img = ImageIO.read(file); 
argbImg = new BufferedImage(img.getWidth(), img.getHeight(), TYPE_INT_ARGB_PRE); 
g = argbImg.createGraphics(); 
g.drawImage(img, 0, 0, null); 
texture = TextureIO.newTexture(argbImg, false); 

Cualquier más métodos de carga (ya sea imágenes de archivo, o imágenes a OpenGL) sería apreciada, voy a actualizar estos puntos de referencia.

+0

El mismo punto de referencia ejecutado en C# utilizando Tao toma avg 1106ms, total 5531ms.Todavía 5 veces más rápido que el método más rápido que he encontrado para Java/JOGL. –

Respuesta

7

Respuesta corta Las clases de texturas JOGL son bastante más de lo necesario, y creo que es por eso que son lentas. Me encontré con el mismo problema hace unos días y ahora lo solucioné cargando la textura con la API de bajo nivel (glGenTextures, glBindTexture, glTexParameterf y glTexImage2D). El tiempo de carga disminuyó de aproximadamente 1 segundo a "ningún retraso notable", pero no he realizado ningún perfil sistemático.

Respuesta larga Si nos fijamos en el código de la documentación y el origen de la JOGL TextureIO, TextureData y clases de textura, observa que lo hacen un poco más que la posibilidad de subir la textura en la GPU:

  • manejo de diferentes formatos de imagen
  • Alfa premultiplicación

no estoy seguro de cuál de ellos está tomando más tiempo. Pero en muchos casos, usted sabe qué tipo de datos de imagen tiene disponibles, y no necesita hacer ninguna premultiplicación.

La característica de premultiplicación alfa está fuera de lugar completamente fuera de lugar en esta clase (desde la perspectiva de la arquitectura del software), y no encontré ninguna forma de deshabilitarla. Aunque la documentación afirma que esta es la "forma matemáticamente correcta" (en realidad, no estoy convencido de eso), hay muchos casos en los que no desea utilizar la premultiplicación alfa, o lo ha hecho de antemano (p. Ej., Para razones de rendimiento).

Después de todo, cargar una textura con la API de bajo nivel es bastante simple a menos que necesite manipular diferentes formatos de imagen. Aquí hay un código Scala, que funciona muy bien para todos mis imágenes de textura RGBA:

val textureIDList = new Array[Int](1) 
gl.glGenTextures(1, textureIDList, 0) 
gl.glBindTexture(GL.GL_TEXTURE_2D, textureIDList(0)) 
gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) 
gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR) 
val dataBuffer = image.getRaster.getDataBuffer // image is a java.awt.image.BufferedImage (loaded from a PNG file) 
val buffer: Buffer = dataBuffer match { 
    case b: DataBufferByte => ByteBuffer.wrap(b.getData) 
    case _ => null 
} 
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, image.getWidth, image.getHeight, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, buffer) 

... 

gl.glDeleteTextures(1, textureIDList, 0) 
+0

Hola, ¿puedes explicar mejor el pasaje de la imagen almacenada en búfer al búfer? No puedo entenderlo ... – elect

1

No estoy seguro de que cierre por completo la brecha de rendimiento, pero debería poder usar el método ImageIO.read que toma un InputStream y pasa un BufferedInputStream envolviendo un FileInputStream. Esto debería reducir en gran medida la cantidad de llamadas de E/S de archivos nativos que la JVM debe realizar. Se vería así:

 
File file = new File(fileName); 
FileInputStream fis = new FileInputStream(file); 
BufferedInputStream bis = new BufferedInputStream(fis, 8192); //8K reads 
BufferedImage image = ImageIO.read(bis); 
+0

Gracias por su comentario. En las pruebas de evaluación comparativa encontré que este enfoque funcionaba peor que ImageIO.read (fileName). He editado mi publicación anterior con los resultados de referencia y las variaciones de carga de textura. –

+0

Edward, Gracias por la información de referencia. ¿Puedes poner también un temporizador medio para que podamos ver el% derramado entre la llamada de ImageIO y la llamada de TextureIO? Junte otras preguntas ... ¿Qué JVM está usando? ¿Cuáles son los parámetros de memoria en la JVM? ¿Está ejecutando JVM en JIT o en modo Interpretación? ¿Ha configurado el GC ?, o ¿está ejecutando GC con valores predeterminados? ¿En qué clase de procesador está ejecutando esto? ¿Alguna observación de CPU/IO vinculada a su sistema durante el benchmark? ¿Su JVM admite AOT de clases? –

+0

Estoy ejecutando java 1.6.0_15 (b03) con Hotspot VM diciendo 14.1-b02 modo mixto, desde Win7 de 64 bits (pero ejecutando la carpeta de 32 bits de dist). No he tocado la configuración del GC, y no estoy seguro de qué se está ejecutando en la JVM: configuración predeterminada, "modo mixto", supongo que implica algo, pero no estoy seguro de qué. Un núcleo es 100% mientras que la evaluación comparativa - aquí hay resultados de referencia más detallados - http://pastebin.mozilla.org/692710 –

1

¿Has mirado en JAI (Java Advanced Imaging), por casualidad, se implementa la aceleración nativa para tareas tales como PNG compresión/descompresión. La implementación de Java de descompresión PNG puede ser el problema aquí. ¿Qué versión de jvm estás usando?

Trabajo con aplicaciones que cargan y renderizan miles de texturas, para esto utilizo una implementación pura de Java de formato DDS, disponible con NASA WorldWind. Las texturas DDS se cargan en GL más rápido ya que la tarjeta gráfica las entiende.

Aprecio su evaluación comparativa y me gustaría utilizar sus experimentos para probar los tiempos de carga de DDS. También modifique la memoria disponible para JAI y JVM para permitir la carga de más segmentos y la descompresión.

1

En realidad, cargo mis texturas en JOGL así:

TextureData data = TextureIO.newTextureData(stream, false, fileFormat); 
Texture2D tex = new Texture2D(...); // contains glTexImage2D 
tex.bind(g); 
tex.uploadData(g, 0, data); // contains glTexSubImage2D 

texturas de carga de esta manera pueden pasar por alto el trabajo extra para contructing un BufferedImage e interpretarla. Es bastante rápido para mí. U puede perfilarlo. Estoy esperando tu resultado.

0

también se puede tratar de cargar la textura directamente de un BufferedImage Hay una example here.

Usando esto puede ver si la carga de la imagen se está tomando el tiempo, o la escritura en Crear/Memoria de video.

Quizá desee considerar el tamaño de la imagen con una potencia 2, es decir, 16,32,64,128,256,1024 ... dimensiones, alguna tarjeta gfx no podrá procesar tamaños sin potencia 2, y usted obtendrá texturas en blanco cuando se usa en esas tarjetas gfx.

Cuestiones relacionadas