Avoid table overhead by using Data when creating a mesh

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
wan-may
Prole
Posts: 5
Joined: Thu Sep 14, 2023 5:06 am

Avoid table overhead by using Data when creating a mesh

Post by wan-may »

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.
User avatar
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

Post by zorg »

- 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.
Me and my stuff :3True 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.
RNavega
Party member
Posts: 355
Joined: Sun Aug 16, 2020 1:28 pm

Re: Avoid table overhead by using Data when creating a mesh

Post by RNavega »

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
wan-may
Prole
Posts: 5
Joined: Thu Sep 14, 2023 5:06 am

Re: Avoid table overhead by using Data when creating a mesh

Post by wan-may »

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?
RNavega
Party member
Posts: 355
Joined: Sun Aug 16, 2020 1:28 pm

Re: Avoid table overhead by using Data when creating a mesh

Post by RNavega »

Unorm16: according to this answer, on older OpenGL versions all data types are mapped to floats. So in your case, it would be a vec2 indeed.
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.
User avatar
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

Post by zorg »

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.
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.

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 :3True 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.
RNavega
Party member
Posts: 355
Joined: Sun Aug 16, 2020 1:28 pm

Re: Avoid table overhead by using Data when creating a mesh

Post by RNavega »

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...
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.
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.

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:
Screenshot_20240405-074516_Drive.jpg
Screenshot_20240405-074516_Drive.jpg (267.58 KiB) Viewed 1128 times
Screenshot_20240405-081118_Drive.jpg
Screenshot_20240405-081118_Drive.jpg (175.93 KiB) Viewed 1125 times
Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests