Загрузка PNG в вопросы производительности OpenGL - Java & JOGL намного медленнее, чем C # и Tao.OpenGL - PullRequest
7 голосов
/ 18 декабря 2009

Я заметил большую разницу в производительности между Java & JOGL и C # & Tao.OpenGL, когда обе загрузки PNG из хранилища в память и при загрузке этого BufferedImage (java) или Bitmap (C # - оба являются PNG на жестком диске) ' в 'OpenGL.

Эта разница довольно велика, поэтому я предположил, что делал что-то не так, однако, после долгих поисков и попыток использования различных методов загрузки, я не смог уменьшить эту разницу.

С Java я получаю изображение, загруженное за 248 мс и загруженное в OpenGL за 728 мс То же самое на C # занимает 54 мс для загрузки изображения и 34 мс для загрузки / создания текстуры.

Вышеуказанное изображение представляет собой PNG-файл с прозрачностью размером 7200x255, используемый для двумерного анимированного спрайта. Я понимаю, что размер действительно довольно нелепый, и я собираюсь сократить спрайт, однако большая разница все еще существует (и сбивает с толку).

На стороне 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:

Я обнаружил, что создание нового BufferedImage с форматом TYPE_INT_ARGB_PRE уменьшает время загрузки текстуры 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) относится к технике, описанной в ответе Джеймса Бранигана ниже. 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), я обновлю эти тесты.

Ответы [ 5 ]

7 голосов
/ 11 мая 2010

Короткий ответ Классы текстур JOGL делают немного больше, чем необходимо, и я думаю, поэтому они медленные. Я столкнулся с той же проблемой несколько дней назад и теперь исправил ее, загрузив текстуру с помощью низкоуровневого API (glGenTextures, glBindTexture, glTexParameterf и glTexImage2D). Время загрузки уменьшилось с 1 секунды до «без заметной задержки», но я не проводил систематического профилирования.

Длинный ответ Если вы посмотрите документацию и исходный код классов JOGL TextureIO, TextureData и Texture, вы заметите, что они делают гораздо больше, чем просто загружают текстуру в графический процессор:

  • Обработка различных форматов изображений
  • Альфа-предварительное умножение

Я не уверен, какой из них занимает больше времени. Но во многих случаях вы знаете, какие данные изображения у вас есть, и вам не нужно выполнять предварительное умножение.

Функция предварительного умножения альфа в любом случае совершенно неуместна в этом классе (с точки зрения архитектуры программного обеспечения), и я не нашел способа ее отключить. Несмотря на то, что в документации утверждается, что это «математически правильный путь» (я на самом деле не уверен в этом), есть много случаев, когда вы не хотите использовать предварительное умножение альфа или делали это заранее (например, для причины производительности).

В конце концов, загрузка текстуры с помощью низкоуровневого 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 голос
/ 22 декабря 2009

Вы когда-нибудь заглядывали в JAI (Java Advanced Imaging), он реализует собственное ускорение для таких задач, как сжатие / распаковка png. Реализация Java декомпрессии PNG может быть проблемой здесь. Какую версию jvm вы используете?

Я работаю с приложениями, которые загружают и визуализируют тысячи текстур, для этого я использую чистую Java-реализацию формата DDS - доступную в NASA WorldWind. Текстуры DDS загружаются в GL быстрее, так как это понимает видеокарта.

Я ценю ваш сравнительный анализ и хотел бы использовать ваши эксперименты для проверки времени загрузки DDS. Также настройте память, доступную для JAI и JVM, чтобы разрешить загрузку большего количества сегментов и распаковку.

1 голос
/ 20 января 2010

На самом деле, я загружаю свои текстуры в 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 и его интерпретации. Это довольно быстро для меня. Вы можете профилировать это. я жду вашего результата.

1 голос
/ 20 декабря 2009

Я не уверен, что это полностью сократит разрыв в производительности, но вы должны быть в состоянии использовать метод ImageIO.read, который принимает InputStream и передает BufferedInputStream, оборачивающий FileInputStream. Это должно значительно сократить количество вызовов файлового ввода-вывода, которые должна выполнять JVM. Это будет выглядеть так:

File file = new File(fileName);
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis, 8192); //8K reads
BufferedImage image = ImageIO.read(bis);
0 голосов
/ 10 апреля 2012

Вы также можете попробовать загрузить текстуру непосредственно из BufferedImage Здесь есть пример .

Используя это, вы можете увидеть, занимает ли загрузка изображения время или выполнить запись в Create / Video Memory.

Вы также можете подумать о размере изображения до степени 2, то есть 16,32,64,128,256,1024 ... размеры, некоторые карты GFX не смогут обрабатывать не степени 2, и вы будете получить пустые текстуры при использовании этих карт gfx.

...