The usual prototype for love.graphics.newMesh has you making a large number of tables: one for each vertex.
This isn't ideal when I could be loading large bespoke third-party models, especially if I'm not interested in changing any vertex data on the CPU.
Can I use ByteData or FFI buffers everywhere instead of tables by calling love.graphics.newMesh( vertexformat, vertexcount, mode, usage ), Mesh:setVertices( data, startvertex, count ), and Mesh:setVertexMap( data, datatype )?
If I specify a custom vertexformat in newMesh, then how do I go about making a Data object that matches the custom vertexformat? The result from Mesh:getvertexformat() seems clear enough, but I'm second guessing whether I need to pad for struct alignment or something.
Avoid table overhead by using Data when creating a mesh
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
- zorg
- Party member
- Posts: 3465
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: Avoid table overhead by using Data when creating a mesh
- newMesh has two variants (one with and one without specifying a custom format) where you can just set a vertex count without defining the vertices with tables. This is something you mentioned.
- Both setVertices and setVertexMap methods support Data objects. This is also something you mentioned.
The actual question that remains is the last one; You need to create a ByteData object with a size enough to contain all your vertices with the format you want; no padding is necessary, but you do need to use the FFI to create a struct with the correct format that you'll use to manipulate the memory area the created ByteData occupies (by getting its pointer with Data:getFFIPointer)
Data:getFFIPointer has a partial snippet you can adapt to your use-case, by defining a struct.
Do note that what getVertexFormat returns is not directly useable when either calculating the size of the ByteData, nor when creating the struct. What i'd do is to first create the struct, so you can functionally get its size (details on the LuaJIT website), then create a ByteData, and finally make a pointer using the struct as its type, pointing to the memory address returned by ByteData:getFFIPointer.
After that, wherever you're getting your mesh data from, if the formats match, you can just write it directly into the bytedata object.
- Both setVertices and setVertexMap methods support Data objects. This is also something you mentioned.
The actual question that remains is the last one; You need to create a ByteData object with a size enough to contain all your vertices with the format you want; no padding is necessary, but you do need to use the FFI to create a struct with the correct format that you'll use to manipulate the memory area the created ByteData occupies (by getting its pointer with Data:getFFIPointer)
Data:getFFIPointer has a partial snippet you can adapt to your use-case, by defining a struct.
Do note that what getVertexFormat returns is not directly useable when either calculating the size of the ByteData, nor when creating the struct. What i'd do is to first create the struct, so you can functionally get its size (details on the LuaJIT website), then create a ByteData, and finally make a pointer using the struct as its type, pointing to the memory address returned by ByteData:getFFIPointer.
After that, wherever you're getting your mesh data from, if the formats match, you can just write it directly into the bytedata object.
Me and my stuff True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Re: Avoid table overhead by using Data when creating a mesh
Using a flat array of attributes set by pointer index timed a bit faster than using an array of structs. But using structs leads to less cumbersome code, as instead of "myAttributes[vertexOffset + attributeOffset] = value", you'd do "myVertex.attribute = value".
So unfortunately it's a trade-off between readability or speed.
You can find some reference code in here:
https://github.com/RNavega/2DMeshAnimat ... pu.lua#L54
So unfortunately it's a trade-off between readability or speed.
You can find some reference code in here:
https://github.com/RNavega/2DMeshAnimat ... pu.lua#L54
Re: Avoid table overhead by using Data when creating a mesh
Thank you both! Love the thorough explanation of these functions, as well as the working, benchmarked example code!
Just to make something explicit for posterity: struct alignment is not a huge concern because LOVE 11.5 does not support custom vertex attributes that aren't aligned to 4 bytes, or that have more than four components (newMesh throws an error).
I still have a couple questions, if that's OK.
What types best correspond to a unorm16 with two components? Is it attribute vec2 on the GLSL side, and a couple of uint16_t's on the C side? I'm especially fuzzy on endianness:
- I pass 0xFEFFFFFF from a ByteData to setVertices, to the attribute {'foo', 'unorm16', 2}. If my CPU is big endian, what values do I get for foo.x and foo.y in my shader?
- What if it's little-endian?
Can I avoid interleaving my vertex attributes by calling mesh:attachAttribute()? For example, can I create a mesh with nothing but an array of the vertex positions, a second mesh with nothing but an array of the UV coordinates, a third mesh with nothing but the vertex colours, then create a fourth mesh for rendering just by attaching (per-vertex) the attributes from the three previous meshes?
Just to make something explicit for posterity: struct alignment is not a huge concern because LOVE 11.5 does not support custom vertex attributes that aren't aligned to 4 bytes, or that have more than four components (newMesh throws an error).
I still have a couple questions, if that's OK.
What types best correspond to a unorm16 with two components? Is it attribute vec2 on the GLSL side, and a couple of uint16_t's on the C side? I'm especially fuzzy on endianness:
- I pass 0xFEFFFFFF from a ByteData to setVertices, to the attribute {'foo', 'unorm16', 2}. If my CPU is big endian, what values do I get for foo.x and foo.y in my shader?
- What if it's little-endian?
Can I avoid interleaving my vertex attributes by calling mesh:attachAttribute()? For example, can I create a mesh with nothing but an array of the vertex positions, a second mesh with nothing but an array of the UV coordinates, a third mesh with nothing but the vertex colours, then create a fourth mesh for rendering just by attaching (per-vertex) the attributes from the three previous meshes?
Re: Avoid table overhead by using Data when creating a mesh
In the OpenGL wiki it says that there are other GLSL data types like int, uint, ivecn, uvecn and dvecn, so I think that after some GLSL version those new types became available. Löve 11.x supports at most GLSL 3.x (see the wiki for love.graphics.newShader), and according to this, the GLSL versions before 4 had only floats (and float vec's) available.
So I think it's safer to just declare the number inputs in your shader as either float and vec2/3/4, as well as the matrix types.
Edit: read zorg's post below.
For endianess, I think the storage itself is agnostic / uses whatever the platform has. If the values are coming from a Lua literal like "249", while that's big-endian in the script ("human friendly"), once it's parsed and stored then LuaJIT, Löve and OpenGL will all use whatever endianness that the platform is in, and when you access that number in a shader, it will have the expected value ("two hundred and forty nine", whatever the endianness). The same for values calculated within Lua.
If the values are being read from a file then you need to interpret the bytes from the file with the endianness that the file was written with, like with Lua's string.unpack() or manually by reading each byte value and combining them with multiplications and sums, or with LuaJIT's built-in "bit" module which is very useful.
I think your seperate mesh attributes setup should work, but I haven't tested it.
Last edited by RNavega on Fri Apr 05, 2024 11:08 am, edited 1 time in total.
- zorg
- Party member
- Posts: 3465
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: Avoid table overhead by using Data when creating a mesh
That site just says that double precision floats are not available before GLSL 4.x, which is almost correct, but the ARB_gpu_shader_fp64 extension might still give support for it regardless.RNavega wrote: ↑Thu Apr 04, 2024 10:18 am In the OpenGL wiki it says that there are other GLSL data types like int, uint, ivecn, uvecn and dvecn, so I think that after some GLSL version those new types became available. Löve 11.x supports at most GLSL 3.x (see the wiki for love.graphics.newShader), and according to this, the GLSL versions before 4 had only floats (and float vec's) available.
So I think it's safer to just declare the number inputs in your shader as either float and vec2/3/4, as well as the matrix types.
The OpenGL wiki does list more types that should be available even before OpenGL 4.0, and that includes integer scalars and vectors as well.
This wiki page on snorm and unorm also confirms that it's effectively just a float, so you can use floats instead.
Me and my stuff True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Re: Avoid table overhead by using Data when creating a mesh
Wow, you're right. Thanks for the correction.
I don't know why I said all of that, when a few days ago I had read the Löve source code and seen how it differentiates the possible uniform types (int, float etc), and used int in a shader myself...
Instead of trying to piece together from separate sources, I should've just consulted the GLSL specification, of which version 1.10 (for OpenGL 2.0, 2004), clearly says that there are different data types and that they can be used as uniforms:
I don't know why I said all of that, when a few days ago I had read the Löve source code and seen how it differentiates the possible uniform types (int, float etc), and used int in a shader myself...
My concern is that it doesn't say what GL/GLSL version it's referring to, so unless there's like this convention of "if no version is specified, then this info applies to all of them", you could end up reading something that is only available in higher versions.zorg wrote: ↑Fri Apr 05, 2024 6:04 am The OpenGL wiki does list more types that should be available even before OpenGL 4.0, and that includes integer scalars and vectors as well.
Instead of trying to piece together from separate sources, I should've just consulted the GLSL specification, of which version 1.10 (for OpenGL 2.0, 2004), clearly says that there are different data types and that they can be used as uniforms:
Who is online
Users browsing this forum: Bing [Bot], Google [Bot] and 10 guests