2009-12-18 82 views
7

我注意到Java和其他編程語言的差別很大, BufferedImage(java)或Bitmap(C# - 都是硬盤上的PNG)'轉換成'OpenGL'。將PNG加載到OpenGL性能問題 - Java和JOGL比C#&Tao慢很多。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,然後渲染以前加載圖像。

編輯3: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

使用濤在C#中的相同的基準測試程序運行需要平均1106ms,總5531ms。仍然比我在Java/JOGL中找到的最快方法快5倍。 – 2009-12-21 00:50:25

回答

7

簡短回答 JOGL紋理類做了比必要的多一點,我想這就是爲什麼他們很慢。我幾天前遇到了同樣的問題,現在通過加載低級API(glGenTextures,glBindTexture,glTexParameterf和glTexImage2D)的紋理來修復它。加載時間從大約1秒減少到「沒有明顯延遲」,但我沒有做任何系統分析。

長的答案 如果你看看JOGL TextureIO,TextureData和紋理類的文檔和源代碼,您會發現他們要做的不僅僅是上傳紋理到GPU相當多的:

  • 處理的不同的圖像格式
  • 阿爾法預乘

我不知道其中哪一個是花更多的時間。但在很多情況下,您會知道您有哪些可用的圖像數據,並且不需要執行任何預乘。

的Alpha預乘功能反正完全在這個類中(從軟件架構的角度)放錯了地方,我沒有找到任何方法來禁用它。儘管文檔聲稱這是「數學上正確的方式」(實際上我並不相信),但有很多情況下您不希望使用alpha預乘,或者事先已經完成(例如for性能原因)。

畢竟,加載與低級別的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) 
+0

嗨,你能解釋一下從緩衝圖像到緩衝區的過程嗎?我無法得到它.. – elect 2014-02-28 13:35:48

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); 
+0

感謝您的評論。 在基準測試中,我發現這種方法執行比ImageIO.read(fileName)更糟糕。我已經用基準結果和紋理負載變化編輯了我的帖子。 – 2009-12-20 22:49:49

+0

Edward, 感謝您的基準信息。你是否也可以使用中間定時器,以便我們可以看到ImageIO調用和TextureIO調用之間溢出的%夫婦其他問題... 您使用的是什麼JVM? JVM上的內存參數是什麼? 您是在JIT還是在解釋模式下運行JVM? 您配置了GC嗎?還是使用默認值運行GC? 你在使用哪種類型的處理器? 在基準測試期間您的系統上CPU/IO綁定的任何意見? 您的JVM是否支持AOT類? – 2009-12-24 00:34:08

+0

我運行java 1.6.0_15(b03)與Hotspot虛擬機說14.1-b02混合模式,從Win7 64位(但運行32位dist文件夾)。 我還沒有觸及GC設置,並且不確定JVM運行的是什麼 - 默認設置,「混合模式」我想暗示一些東西,但不知道是什麼! 一個核心是100%,而基準測試 - 這裏有更詳細的基準測試結果 - http://pastebin.mozilla.org/692710 – 2009-12-25 02:32:40

1

你有任何機會看着JAI(Java高級圖像),它實現這樣的任務,PNG壓縮/解壓縮本地加速度。 PNG解壓縮的Java實現可能是這裏的問題。你正在使用哪個版本的jvm?

我與應用程序加載哪些工作,使成千上萬的紋理,爲了這個,我使用DDS格式的純Java實現 - 可與美國航天局世界風。由於圖形卡可以理解DDS紋理加載到GL的速度更快。

我很欣賞你的標杆,並想用您的實驗來測試DDS加載時間。還可以調整可用於JAI和JVM的內存,以允許加載更多的段和解壓縮。

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 

以這種方式加載紋理可以繞過額外的工作contructing一個BufferedImage和解釋它。 這對我來說非常快。你可以將其剖析出來。即時消息等待你的結果。

0

你也可以嘗試直接從一個BufferedImage 載入紋理有一個example here

利用這一點,你可以看到,如果圖像負載抽空,或創建/顯存寫入。

您可能還想考慮圖像的大小爲2,即16,32,64,128,256,1024 ...尺寸,某些gfx卡將無法處理非功率2尺寸,而您在這些gfx卡上使用時會得到空白紋理。