7

両方ともストレージからメモリにPNGをロードするとき、およびそのBufferedImage(java)またはビットマップ(C#-両方ともハードドライブ上のPNG)をOpenGLにロードするとき、JavaとJOGLとC#とTao.OpenGLのパフォーマンスに大きな違いがあることに気づきました。 。

この違いは非常に大きいので、何か間違ったことをしていると思いましたが、さまざまな読み込み手法を何度も検索して試した結果、この違いを減らすことができませんでした。

Javaを使用すると、画像が248ミリ秒で読み込まれ、OpenGLに728ミリ秒で読み込まれます。C#でも同じように、画像の読み込みに54ミリ秒、テクスチャの読み込み/作成に34ミリ秒かかります。

上記の画像は、2Dアニメーションスプライトに使用される7200x255のサイズの透明度を含むPNGです。サイズが本当にばかげていることに気づき、スプライトを切り取ることを検討していますが、大きな違いはまだあります(そして混乱します)。

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)は、以下のJamesBraniganの回答で説明されている手法を指します。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への画像のいずれか)をいただければ幸いです。これらのベンチマークを更新します。

4

5 に答える 5

7

簡単な回答 JOGL テクスチャ クラスは、必要以上に多くのことを行います。それが遅い理由だと思います。数日前に同じ問題に遭遇しましたが、低レベル API (glGenTextures、glBindTexture、glTexParameterf、および glTexImage2D) を使用してテクスチャをロードすることで修正しました。読み込み時間は約 1 秒から「目立った遅延なし」に短縮されましたが、体系的なプロファイリングは行っていません。

長い回答 JOGL TextureIO、TextureData、Texture クラスのドキュメントとソース コードを調べると、GPU にテクスチャをアップロードするだけでなく、かなり多くのことを行っていることがわかります。

  • 異なる画像フォーマットの取り扱い
  • アルファ前乗算

どちらがより多くの時間を取っているかはわかりません。しかし、多くの場合、利用可能なイメージ データの種類がわかっているため、前乗算を行う必要はありません。

いずれにせよ、このクラスでは (ソフトウェア アーキテクチャの観点から) アルファ プリマルチプリケーション機能が完全に見当違いであり、それを無効にする方法が見つかりませんでした。ドキュメントはこれが「数学的に正しい方法」であると主張していますが (私は実際にはそれについて確信が持てません)、アルファ事前乗算を使用したくない場合や、事前にそれを行っている場合がたくさんあります (たとえば、パフォーマンス上の理由)。

結局のところ、低レベル API を使用してテクスチャをロードするのは、さまざまな画像フォーマットを処理する必要がない限り、非常に簡単です。これは、すべての RGBA テクスチャ イメージで適切に機能する scala コードです。

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)
于 2010-05-10T21:33:30.990 に答える
1

パフォーマンスのギャップを完全に埋めるかどうかはわかりませんが、InputStream を受け取り、FileInputStream をラップする BufferedInputStream を渡す ImageIO.read メソッドを使用できるはずです。これにより、JVM が実行しなければならないネイティブ ファイル I/O 呼び出しの数が大幅に削減されます。次のようになります。

ファイル file = new File(fileName);
FileInputStream fis = new FileInputStream(ファイル);
BufferedInputStream bis = new BufferedInputStream(fis, 8192); //8K読み取り
BufferedImage 画像 = ImageIO.read(bis);
于 2009-12-20T01:55:16.517 に答える
1

ひょっとしてJAI (Java Advanced Imaging)を調べたことはありますか? png 圧縮/解凍などのタスクのネイティブ アクセラレーションを実装しています。ここでは、PNG 解凍の Java 実装が問題になる可能性があります。どのバージョンの jvm を使用していますか?

私は何千ものテクスチャを読み込んでレンダリングするアプリケーションを扱っています。このために、NASA WorldWind で利用可能な DDS 形式の純粋な Java 実装を使用しています。DDS テクスチャは、グラフィックス カードによって認識されるため、GL への読み込みが速くなります。

ベンチマークに感謝し、実験を使用して DDS の読み込み時間をテストしたいと思います。また、JAI と JVM で使用できるメモリを微調整して、より多くのセグメントのロードと圧縮解除を可能にします。

于 2009-12-22T13:53:25.560 に答える
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 を作成して解釈するための余分な作業を回避できます。私にとってはかなり速いです。U はそれをプロファイリングできます。あなたの結果を待っています。

于 2010-01-20T09:19:55.817 に答える
0

BufferedImageから直接テクスチャをロードすることもできます。ここに例があります。

これを使用して、画像の読み込みに時間がかかっているかどうか、または作成/ビデオメモリへの書き込みに時間がかかっているかどうかを確認できます。

また、画像のサイズを2の累乗、つまり16、32、64、128、256、1024 ...のサイズで検討することもできます。一部のgfxカードは、2の累乗以外のサイズを処理できず、空白のテクスチャが表示されます。それらのgfxカードで使用する場合。

于 2012-04-10T09:12:20.903 に答える