Vulkan-docs: Does the data of vkCmdUpdateBuffer have to be 4-bytes aligned?

Created on 23 Jun 2016  路  8Comments  路  Source: KhronosGroup/Vulkan-Docs

The data that you pass to vkCmdUpdateBuffer is of type const uint32_t*.

My question is whether that pointer must be 4-bytes aligned, or are you able to take unaligned data and cast it to a const uint32_t* before passing it?

On one hand the specs doesn't say anything about the alignment of the data, which means that presumably it doesn't matter.
On the other hand, the compiler of an implementation that uses a const uint32_t* may assume that the pointer is 4-bytes aligned and perform optimizations based on that.

More generally, the choice of using a const uint32_t* instead of a const void* here is weird and should maybe be explicited.

Most helpful comment

The fundamentals chapter says:

"Any parameter that is a pointer must: be a valid pointer. A pointer is valid
if it points at memory containing values of the number and type(s) expected
by the command, and all fundamental types accessed through the pointer (e.g.
as elements of an array or as members of a structure) satisfy the alignment
requirements of the host processor."

Thus, if the host disallows unaligned memory accesses (like some ARM systems), then it is illegal. More generally, it's up to the language and platform.

All 8 comments

Another point that was raised on IRC is that casting from a pointer to a type to a pointer of a different type is maybe undefined behavior in C, except for char and void (I say maybe because I'm not actually sure).

If that's the case, then this would mean that technically the user is limited to using only uint32_t for vkCmdUpdateBuffer and that for example uploading floats would be undefined behavior.

C99 spec (draft) 6.3.2.3.7:

A pointer to an object or incomplete type may be converted to a pointer to a different
object or incomplete type. If the resulting pointer is not correctly aligned57) for the
pointed-to type, the behavior is undefined.

besides some platforms can't even read unaligned data directly and some only with serious performance hit.

The fundamentals chapter says:

"Any parameter that is a pointer must: be a valid pointer. A pointer is valid
if it points at memory containing values of the number and type(s) expected
by the command, and all fundamental types accessed through the pointer (e.g.
as elements of an array or as members of a structure) satisfy the alignment
requirements of the host processor."

Thus, if the host disallows unaligned memory accesses (like some ARM systems), then it is illegal. More generally, it's up to the language and platform.

Another point that was raised on IRC is that casting from a pointer to a type to a pointer of a different type is maybe undefined behavior in C, except for char and void (I say maybe because I'm not actually sure).

I think the casting to any arbitrary pointer type is okay (as long as it satisfies the alignment requirement quoted earlier), it's only undefined behaviour if you dereference it while it's a type other than the data's declared type (or some other types if it has no declared type) (or as char, unsigned char, etc).

That means that e.g.

float data[] = { 1, 2, 3, 4 };
vkCmdUpdateBuffer(cmd, buf, 0, sizeof(data), (const uint32_t *)data);

is a legal call to make, but if vkCmdUpdateBuffer is written in C and it dereferences pData as uint32_t* (or any type other than float* or char*) then it's triggering undefined behaviour.

But the above code also violates the valid usage requirement:

pData must be a pointer to an array of dataSize/4 uint32_t values

because it's clearly not an array of uint32_t values here. You'd have to write:

float data[] = { 1, 2, 3, 4 };
uint32_t data2[4];
memcpy(data2, data, sizeof(data2));
vkCmdUpdateBuffer(cmd, buf, 0, sizeof(data2), data2);

to meet the requirements.

Is it really intentional that you can't sanely use vkCmdUpdateBuffer for a buffer containing floats?

Is it really intentional that you can't sanely use vkCmdUpdateBuffer for a buffer containing floats?

Let's be realistic here. Despite being undefined behavior, your original code will most assuredly _work_. So while it's a theoretical problem, it's not a problem in the real world.

The larger question is this: do the makers of Vulkan intend for vkCmdUpdateBuffer to work only on pointers which match uint32_t's alignment? And if so, is the best way to do that by making the pointer a pointer to a uint32_t, rather than specifying the alignment requirement of the pointer explicitly (and thus allowing it to be a general void*).

I think it would be better to take a void* and simply declare that the pointer must be four-byte aligned. After all, the offset and sizes have to be 4-byte aligned too.

FWIW, I think the type is uint32_t* "for historical reasons", rather than being a conscious decision that we made.

We understand there are various arcane edge-condition C language things that could in principle make it hard to implement Vulkan. But these issues come from ancient or researchy architectures of no commercial interest, not things people actually build and deploy at scale today. If we have to add more verbiage near the "Architecture Model" section to make this even more clear, we will. But as none of the hardware manufacturers or system integrators in Khronos are worrying about this, users probably shouldn't worry either.

We convinced ourselves it would be harmless to change the type to void * in 1.0.19.

Was this page helpful?
0 / 5 - 0 ratings