2009-12-18 23 views
7

Tôi nhận thấy sự khác biệt hiệu suất lớn giữa Java & JOGL và C# & Tao.OpenGL khi tải PNG từ bộ nhớ vào bộ nhớ và khi tải BufferedImage (java) hoặc Bitmap (C# - cả hai đều là PNG trên ổ cứng) 'vào' OpenGL.Tải PNG thành các vấn đề về hiệu năng OpenGL - Java & JOGL chậm hơn nhiều so với C# & Tao.OpenGL

Sự khác biệt này khá lớn, vì vậy tôi cho rằng mình đã làm điều gì đó sai, tuy nhiên sau khá nhiều tìm kiếm và thử các kỹ thuật tải khác nhau, tôi không thể giảm sự khác biệt này.

Với Java, tôi tải hình ảnh trong 248ms và tải vào OpenGL trong 728ms Tương tự trên C# mất 54ms để tải hình ảnh và 34ms để tải/tạo kết cấu.

Hình ảnh được đề cập ở trên là PNG có độ trong suốt, có kích thước 7200x255, được sử dụng cho một hình động 2D. Tôi nhận ra kích thước thực sự là khá vô lý và đang xem xét cắt lên các sprite, tuy nhiên sự khác biệt lớn vẫn còn đó (và khó hiểu).

Về phía Java mã trông như thế này:

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

Mã C# sử dụng:

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(); 

Sau khá nhiều thử nghiệm tôi chỉ có thể đi đến kết luận rằng Java/JOGL chỉ chậm hơn ở đây - việc đọc PNG có thể không nhanh hoặc tôi vẫn đang làm điều gì sai.

Cảm ơn.

Edit2:

Tôi đã tìm thấy rằng việc tạo một BufferedImage mới với định dạng TYPE_INT_ARGB_PRE giảm thời gian tải kết cấu OpenGL bằng gần một nửa - điều này bao gồm việc phải tạo ra BufferedImage mới, nhận được Graphics2D từ nó và sau đó vẽ các tải trước đây hình ảnh cho nó.

Chỉnh sửa3: Kết quả điểm chuẩn cho 5 biến thể. Tôi đã viết một công cụ đo điểm chuẩn nhỏ, các kết quả sau đây đến từ việc tải một bộ 33 png, hầu hết là rất rộng, 5 lần.

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) dùng để chỉ kỹ thuật được mô tả trong câu trả lời của James Branigan dưới đây. argbImage đề cập đến các kỹ thuật được mô tả trong chỉnh sửa trước đây của tôi:

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

Bất kỳ phương pháp hơn xếp hàng (hoặc hình ảnh từ file, hoặc hình ảnh để OpenGL) sẽ được đánh giá, tôi sẽ cập nhật những tiêu chuẩn.

+0

Cùng một điểm chuẩn chạy trong C# bằng cách sử dụng Tao mất tổng cộng 1106ms, tổng 5531ms.Vẫn nhanh hơn 5 lần so với phương thức nhanh nhất mà tôi đã tìm thấy cho Java/JOGL. –

Trả lời

7

ngắn trả lời Các lớp kết cấu JOGL làm khá hơn một chút hơn mức cần thiết, và tôi đoán đó là lý do tại sao họ là chậm. Tôi chạy vào cùng một vấn đề một vài ngày trước, và bây giờ cố định nó bằng cách tải các kết cấu với các API cấp thấp (glGenTextures, glBindTexture, glTexParameterf, và glTexImage2D). Thời gian tải giảm từ khoảng 1 giây xuống "không có sự chậm trễ đáng chú ý", nhưng tôi chưa thực hiện bất kỳ hồ sơ có hệ thống nào.

dài trả lời Nếu bạn nhìn vào mã tài liệu và nguồn gốc của JOGL TextureIO, TextureData và các lớp Texture, bạn nhận thấy rằng họ làm khá hơn một chút so với chỉ tải lên các kết cấu trên GPU:

  • Xử lý các định dạng hình ảnh khác nhau
  • Alpha premultiplication

tôi không chắc chắn mà một trong những được dành thời gian nhiều hơn nữa. Nhưng trong nhiều trường hợp, bạn biết loại dữ liệu hình ảnh nào bạn có sẵn và không cần thực hiện bất kỳ thao tác nào trước.

Tính năng tiền xử lý alpha vẫn bị thất lạc hoàn toàn trong lớp này (từ góc độ kiến ​​trúc phần mềm) và tôi không tìm thấy cách nào để vô hiệu hóa tính năng này. Mặc dù tài liệu cho rằng đây là "cách toán học chính xác" (tôi thực sự không bị thuyết phục về điều đó), có rất nhiều trường hợp mà bạn không muốn sử dụng tính năng xử lý trước alpha hoặc đã thực hiện trước (ví dụ: lý do hiệu suất).

Sau khi tất cả, tải một kết cấu với API cấp thấp là khá đơn giản, trừ khi bạn cần nó để xử lý các định dạng hình ảnh khác nhau. Dưới đây là một số mã scala mà hoạt động độc đáo cho tất cả các hình ảnh kết cấu RGBA tôi:

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

Xin chào, bạn có thể giải thích tốt hơn đoạn từ BufferedImage đến bộ đệm không? Tôi không thể .. – elect

1

Tôi không chắc chắn rằng nó sẽ đóng hoàn toàn khoảng cách hiệu năng, nhưng bạn sẽ có thể sử dụng phương thức ImageIO.read lấy InputStream và chuyển vào BufferedInputStream gói một FileInputStream. Điều này sẽ làm giảm đáng kể số lượng các cuộc gọi I/O của tệp gốc mà JVM phải thực hiện. Nó sẽ giống như thế này:

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

Cảm ơn nhận xét của bạn. Trong các bài kiểm tra điểm chuẩn, tôi thấy rằng phương pháp này hoạt động kém hơn ImageIO.read (tên tệp). Tôi đã chỉnh sửa bài đăng của mình ở trên với kết quả điểm chuẩn và các biến thể tải kết cấu. –

+0

Edward, Cảm ơn thông tin điểm chuẩn. Bạn có thể đặt một bộ đếm thời gian ở giữa là tốt, để chúng ta có thể thấy% tràn giữa cuộc gọi ImageIO và cuộc gọi TextureIO? Vài câu hỏi khác ... Bạn đang sử dụng JVM nào? Các tham số bộ nhớ trên JVM là gì? Bạn có đang chạy JVM trong JIT hoặc trong chế độ Thông dịch không? Bạn đã định cấu hình GC chưa ?, hoặc bạn đang chạy GC với mặc định? Bạn đang chạy loại bộ vi xử lý nào? Bất kỳ quan sát nào về CPU/IO bị ràng buộc trên hệ thống của bạn trong quá trình benchmark? JVM của bạn có hỗ trợ AOT các lớp học không? –

+0

Tôi đang chạy java 1.6.0_15 (b03) với Hotspot VM nói chế độ hỗn hợp 14.1-b02, từ Win7 64-bit (nhưng chạy thư mục dist 32 bit). Tôi chưa chạm vào cài đặt GC và không chắc chắn JVM đang chạy trong cài đặt mặc định, "chế độ hỗn hợp" tôi đoán ngụ ý điều gì đó nhưng không chắc chắn về điều gì! Một lõi là 100% trong khi đo điểm chuẩn - dưới đây là kết quả điểm chuẩn chi tiết hơn - http://pastebin.mozilla.org/692710 –

1

Các bạn đã nhìn vào JAI (Java Advanced Imaging) bởi bất kỳ cơ hội, nó thực hiện tăng tốc mẹ đẻ cho các nhiệm vụ như png nén/giải nén. Việc thực thi Java giải nén PNG có thể là vấn đề ở đây. Bạn đang sử dụng phiên bản jvm nào?

Tôi làm việc với các ứng dụng tải và hiển thị hàng nghìn hoạ tiết, vì điều này tôi sử dụng cách triển khai Java thuần túy định dạng DDS - có sẵn với NASA WorldWind. DDS Textures tải vào GL nhanh hơn vì nó được hiểu bởi card đồ họa.

Tôi đánh giá cao điểm chuẩn của bạn và muốn sử dụng thử nghiệm của bạn để kiểm tra thời gian tải DDS. Cũng tinh chỉnh bộ nhớ có sẵn cho JAI và JVM để cho phép tải nhiều phân đoạn hơn và giải nén.

1

Trên thực tế, tôi tải kết cấu của tôi trong JOGL như thế này:

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

tải kết cấu theo cách này có thể bỏ qua việc bổ sung cho contructing một BufferedImage và giải thích nó. Nó khá nhanh đối với tôi. U có thể cấu hình nó ra. im chờ kết quả của bạn.

0

bạn cũng có thể thử tải Texture trực tiếp từ một BufferedImage Có một example here.

Sử dụng tính năng này, bạn có thể xem tải hình ảnh có mất thời gian hay không hoặc ghi vào bộ nhớ Tạo/Video.

Bạn cũng có thể muốn suy nghĩ về kích thước của hình ảnh thành lũy thừa 2, tức là 16,32,64,128,256,1024 ... kích thước, một số thẻ gfx sẽ không thể xử lý các kích thước không quyền lực 2 và bạn sẽ nhận được kết cấu trống khi sử dụng trên các thẻ gfx đó.

Các vấn đề liên quan