using GeomVertexArrayDataHandle::set_data directly

hi,

i tried to pass a pointer to a vertex array directly to a GeomVertexData object, as discussed here.

i set up the GeomVertexFormat correctly, containing 2 seperate arrays: one for vertices and one for normals (filling the GeomVertexData via a GeomVertexWriter works so i assume the Format is set up right)

now i have a void * verticesPosBegin which contains the address of the first vertex, and i try to pass this information like this (vdata is the GeomVertexData object):

vdata->modify_array(0)->modify_handle()->set_data((char*)ptr->verticesPosBegin);

compiling works, but at runtime i get this error:
AssertionError: reader.check_valid(data_reader) at line 1733 of c:\temp\mkpr\panda\src\gobj\geom.cxx

any idea what i’m doing wrong?

thanks

The (char *) cast means that you are sending a null-terminated string to the array. Obviously, this is not right for your data, since that means the first zero byte in the data marks the end of the array! You need an interface through which you can pass the pointer and its length. (In Python, the native string class knows its length, rather than being null-terminated, which is why this works in Python.)

One way is to explicitly use the C++ string object (this is the interface you are actually using, maybe without knowing it):

vdata->modify_array(0)->modify_handle()->set_data(string((char*)ptr->verticesPosBegin, ptr->verticesPosEnd - ptr->verticesPosBegin));

But this does involve a double-copy. The string methods are mainly defined for the convenience of high-level languages like Python. In C++, you can simply stuff the data on yourself:

vdata->set_num_rows(num_rows);
char *data_pointer = vdata->modify_array(0)->get_write_pointer();
memcpy(data_pointer, ptr->verticesPosBeing, ptr->verticesPosEnd - ptr->verticesPosBegin);

In all the above, I’m assuming that (ptr->verticesPosEnd - ptr->verticesPosBegin) is the length of your array in bytes, which is only true if both pointers are of type char or unsigned char or some such. If not, cast appropriately before performing the subtraction.

David

thanks david,

this seems to work now, unfortunately performance doesnt benefit from this method. maybe because the whole data has to be copied too (memcpy), like when using a GeomVertexWriter and copy each vertex in a loop?

basically i am reproduing a physx sample scene, which runs smoothly rendered directly in opengl (where pointers to vertex and normal data are passed and nothing has to be copied), but is very slow in panda. so i think this process of copying data has to be the reason for these performance issues.

or am i misunderstanding something?

thanks very much for your help!

The extra copy will impose some cost, true, though I doubt it would make the difference between “smooth” and “very slow”. There must be something else wrong.

You’re not creating a new GeomVertexData every frame, are you? Once you’ve created the GeomVertexFormat and GeomVertexData, you should just re-use it for each new frame’s data. Also, be sure you create the GeomVertexData with the UH_stream (or maybe even UH_client) usage hint.

If that’s not the problem, it will be time to dust off PStats or some other performance-analysis tool.

David

you are right: its a pure physx issue (after turning off panda rendering completely nothing changes…). first i thought it has to do with copying because these problems only occur with large meshes, smaller ones work fine. so copying of these amounts of data each frame doesnt seem to affect performance remarkably, regardless wheter copying data with GeomVertexWriters or via memcpy.

anyway just out of interest:
one way to avoid copying at all would be to allocate memory with a GeomVertexData object, and then tell physx where this memory is - you have to tell physx anyway where it should write out its data via the void * verticesPosBegin.
how could i do that?

regards,
β

Well, that’s precisely what we’re doing above, with these two lines:

vdata->set_num_rows(num_rows);
char *data_pointer = vdata->modify_array(0)->get_write_pointer();

The first line tells your GeomVertexData to ensure it has been preallocated to num_rows rows (this is not necessary if you are already sure that it is). The second line gets a pointer to that data in memory.

You can then use data_pointer for whatever you like. You can memcpy into it, or you can pass it into physx and let it take care of filling it in.

There are only two catches. (1) Panda doesn’t have a concept of locking the memory and guaranteeing that the pointer won’t change, and (2) Panda needs to know whenever you might be updating the data, so it can invalidate its internal caches and reload the vertex data to the graphics card when necessary.

To work around both issues, you should simply call get_write_pointer() again every frame that physx is running. This is Panda’s cue that you intend to modify the data, and it forces the caches to be marked invalid. It also moves the array to the tail of the LRU queue, so that it will be less likely to be evicted and therefore invalidated. (And, in fact, unless you specifically enable vertex paging, it will have zero chance of being evicted anyway.)

David

Oops, I realized that I left out a call in the chain, but I guess you figured this out. The correct call is:

char *data_pointer = vdata->modify_array(0)->modify_handle()->get_write_pointer();

David

thanks guru! it works as you described, no copying of data anymore. but in the scenes i have right now there is no performance improvement. so there is no performance difference in setting ~300 verts and normals each frame via vertex writer, via memcpy or not doing it at all… maybe physx has to do some internal restructuring when setting the verticesPosBegin each frame, or maybe it makes a difference with larger meshes, but i’m having other issues here atm.

thanks anyway