我注意到Java和Java之间存在很大的性能差异。 JOGL和C#& Tao.OpenGL既可以将PNG从存储器加载到内存中,也可以将BufferedImage(java)或Bitmap(C# - 都是硬盘上的PNG)加载到'OpenGL中。
这种差异非常大,所以我认为我做错了,但经过大量的搜索和尝试不同的加载技术后,我一直无法减少这种差异。
使用Java,我得到一个248ms加载的图像,并在728ms内加载到OpenGL中 在C#上加载图像需要54ms,加载/创建纹理需要34ms。
上面讨论的图像是一个包含透明度的PNG,大小为7200x255,用于2D动画精灵。我意识到尺寸确实非常荒谬,并且正在考虑削减精灵,但是差异仍然存在(并且令人困惑)。
在Java方面,代码如下所示:
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);
C#代码使用:
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();
经过大量的测试后,我只能得出Java / JOGL在这里速度较慢的结论 - PNG读取可能不会那么快,或者我仍然做错了。
感谢。
EDIT2:
我发现创建一个格式为TYPE_INT_ARGB_PRE的新BufferedImage会使OpenGL纹理加载时间缩短近一半 - 这包括必须创建新的BufferedImage,从中获取Graphics2D,然后将之前加载的图像渲染到它。
Edit3:5种变化的基准测试结果。 我写了一个小的基准测试工具,下面的结果来自加载一组33个png,大多数是非常宽的,5次。
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)指的是James Branigan在下面的答案中描述的技术。 argbImage是指我之前编辑中描述的技术:
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);
我们将非常感谢加载任何方法(从文件中获取图像,或者将图像加载到OpenGL),我将更新这些基准测试。
答案 0 :(得分:7)
简答 JOGL纹理类比必要的要多得多,我想这就是为什么它们很慢。我几天前遇到了同样的问题,现在通过使用低级API(glGenTextures,glBindTexture,glTexParameterf和glTexImage2D)加载纹理来修复它。加载时间从大约1秒减少到“没有明显的延迟”,但我没有进行任何系统的分析。
长答案 如果你查看JOGL TextureIO,TextureData和Texture类的文档和源代码,你会发现它们不仅仅是将纹理上传到GPU上:
我不确定其中哪一个花费更多时间。但在许多情况下,您知道您可以使用哪种图像数据,并且不需要进行任何预乘。
alpha预乘功能无论如何完全错位在这个类中(从软件架构的角度来看),我没有找到任何方法来禁用它。即使文档声称这是“数学上正确的方式”(我实际上并不相信),但是有很多情况下你不想使用alpha预乘,或者事先已经完成了(例如表现原因)。
毕竟,使用低级API加载纹理非常简单,除非您需要它来处理不同的图像格式。这里有一些scala代码可以很好地适用于我的所有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)
答案 1 :(得分:1)
我不确定它是否会完全缩小性能差距,但您应该能够使用ImageIO.read方法接受InputStream并传入包装FileInputStream的BufferedInputStream。这应该会大大减少JVM必须执行的本机文件I / O调用的数量。它看起来像这样:
File file = new File(fileName); FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis, 8192); //8K reads BufferedImage image = ImageIO.read(bis);
答案 2 :(得分:1)
您是否有机会查看JAI(Java高级成像),它会为png压缩/解压缩等任务实现本机加速。 PNG解压缩的Java实现可能是这里的问题。您使用的是哪个版本的jvm?
我使用加载和渲染数千种纹理的应用程序,为此我使用DDS格式的纯Java实现 - 可与NASA WorldWind一起使用。由于图形卡可以理解DDS纹理加载到GL中更快。
我感谢您的基准测试,并希望使用您的实验来测试DDS加载时间。还调整JAI和JVM可用的内存,以允许加载更多段和解压缩。
答案 3 :(得分:1)
实际上,我在JOGL中加载我的纹理,如下所示:
TextureData data = TextureIO.newTextureData(stream, false, fileFormat);
Texture2D tex = new Texture2D(...); // contains glTexImage2D
tex.bind(g);
tex.uploadData(g, 0, data); // contains glTexSubImage2D
以这种方式加载纹理可以绕过构建BufferedImage并解释它的额外工作。 这对我来说非常快。 你可以把它描出来。我在等你的结果。
答案 4 :(得分:0)
您也可以尝试直接从BufferedImage加载纹理 有example here。
使用此功能,您可以查看图像加载是占用时间还是写入创建/视频内存。
您可能还想将图像的大小考虑到功率2,即16,32,64,128,256,1024 ...尺寸,某些gfx卡将无法处理非功率2尺寸,您将在这些gfx卡上使用时获得空白纹理。