#java #opengl #native #lwjgl
#java #opengl #родной #lwjgl
Вопрос:
Я ищу некоторые рекомендации по памяти / производительности, какой подход лучше.
Если допустим, у меня есть 4 атрибута для сетки
Vertex 3f
Normal 3f
TexCoords 2f
jointID 4i [Integer Joint Indices For Skeleton Animation]
И они мне нужны в памяти процессора, поскольку их можно изменить в любое время
Лучше ли
a. Создайте 4 отдельных буфера для каждого компонента
//3,2,4 are the strides i.e vertex is 3 floats,texCoord is 2 floats so on
FloatBuffer vertices=BufferUtils.createFloatBuffer(numOfVertices*3);
FloatBuffer normals=BufferUtils.createFloatBuffer(numOfVertices*3);
FloatBuffer texCoords=BufferUtils.createFloatBuffer(numOfVertices*2);
IntBuffer vertexJoints=BufferUtils.createIntBuffer(numOfVertices*4);
Или
б. Создайте большой байтбуфер с достаточной емкостью для хранения всех 4 атрибутов и создайте отдельные представления буфера Float / Int для каждого из атрибутов
ByteBuffer meshData=BufferUtils.createByteBuffer(((numOfVertices*3) (numOfVertices*3) (numOfVertices*2) (numOfVertices*4))*4); //*4 because both float/int is 4 bytes
FloatBuffer vertices=meshData.position(0).limit(endVertexByte).asFloatBuffer();
FloatBuffer normals=meshData.position(endVertexByte).limit(endNormalByte).asFloatBuffer();
FloatBuffer texCoords=meshData.position(endNormalByte).limit(endTexCoordByte).asFloatBuffer();
IntBuffer jointIDs=meshData.position(endTexCoordByte).limit(endJointIndexByte or end of buffer in this case).asIntBuffer();
Из документов все методы BufferUtils создают DirectBuffer, который хранится в собственной памяти, и все, хотя 2-й подход создает буфер большего размера [поскольку мы умножаем на 4], чем все отдельные буферы атрибутов вместе взятые, он создает только один большой фрагмент встроенной памяти по сравнению с 4 отдельными областями памяти в первомподход.
Но это только мое мнение, мысли?
Ответ №1:
Разницы в производительности не будет, если мы просто посмотрим, как вы записываете (новые) данные в эти буферы с точки зрения процессора. В любом случае у вас есть только четыре последовательных области памяти, к которым вы подключаетесь при обновлении данных атрибутов вершин. Просто в первом случае эти области памяти смещены на неизвестное количество байтов (поскольку распределитель памяти JVM будет выделять каждую область отдельно), в то время как в последнем случае вы знаете смещение между каждыми двумя последовательными областями памяти, потому что вы выделили их в одном распределении буферной памяти JVM.
Однако разница заключается в том, как вы на самом деле сопоставляете эти области хост-памяти на стороне клиента с памятью буферного объекта OpenGL на стороне сервера. Я полагаю, что после обновления памяти на стороне хоста вы фактически загрузите ее в объекты буфера OpenGL на стороне сервера и не будете использовать указатели памяти на стороне клиента / хоста для команд спецификации вершин OpenGL (которые доступны только в контексте совместимости с OpenGL).
В этом случае для создания четырех отдельных смежных областей памяти на стороне клиента потребуется выполнить четыре команды загрузки буферной памяти OpenGL ( glBufferSubData()
) и драйвер OpenGL для выполнения четырех отдельных загрузок с прямым доступом к памяти (DMA) через PCIe. В случае, когда у вас есть только одна непрерывная область памяти на стороне клиента, вы можете выполнить только один glBufferSubData()
вызов для всех данных атрибутов вершины в один объект buffer, где вы просто используете смещения байтов в вызовах спецификации вершин OpenGL (например, for glVertexAttribPointer()
).
Другая возможность также заключается в том, чтобы не выделять память хоста на стороне клиента самостоятельно, но иметь видимые для хоста, постоянно отображаемые области буфера, предоставленные вам OpenGL ( glBufferStorage()
glMapBufferRange()
), которые затем вы можете записать и явно очистить или позволить им неявно / последовательно обновлять драйвером OpenGL. Как и в случае с четырьмя отдельными областями памяти на стороне клиента, вы также, вероятно, будете платить за «четыре отдельных передачи DMA», когда вы сопоставляете и очищаете четыре отдельных области объектов буфера OpenGL.
Итак, в конце концов, не так важно, есть ли у вас одно или четыре представления буфера NIO в вашей памяти на стороне клиента, но скольким объектам буфера OpenGL на стороне сервера вы сопоставляете эти области памяти — чем меньше, тем лучше.
Комментарии:
1. Итак, 2-й подход лучше, если у вас есть только один glBuffer в случае interleavedBuffer[где мы указываем шаги и смещения в vertexAttribPointer], но не приведет к различию, если у вас есть 4 отдельных объекта glBuffer, если вы хотите сохранить отдельные атрибуты, правильно? glMapBuffer невозможно эффективно использовать в многопоточной настройке, где для выполнения вызовов требуется переключение контекста, и единственный вариант — сначала загрузить данные вершин в cpu, а затем создать glBuffers в основном потоке. Но с точки зрения памяти jni я делаю некоторую экономию?
2. говоря об упаковке всего, является ли чередующийся glBuffer лучше, чем 4 отдельных glBuffers, когда дело доходит до скорости чтения gpu, или это отдельный вопрос для другого потока?
3. Я не совсем уверен, что мне нужно некоторое время для экспериментов, и если мои выводы совпадут с вашими, я отмечу это тогда