0

I have a uniform buffer that is created like this:

const temp = new Float32Array([0, 0, 0, 0, 0, 0, 0, 0]);
const positionBuffer = device.createBuffer({
    size: temp.byteLength,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_SRC,
    mappedAtCreation: true,
});

const positionArray = new Float32Array(positionBuffer.getMappedRange());
positionArray.set(temp);
positionBuffer.unmap();

When I want to update the position uniform, I try to run positionBuffer.mapAsync(GPUMapMode.WRITE) but it fails with the following warning:

The buffer usages (BufferUsage::(CopySrc|Uniform)) do not contain BufferUsage::MapWrite.

But when I then add the MAP_WRITE flag I get a different warning:

Buffer usages (BufferUsage::(MapWrite|CopyDst|Index)) is invalid. If a buffer usage contains BufferUsage::MapWrite the only other allowed usage is BufferUsage::CopySrc.

I obviously need the uniform flag and probably the copy_dst flag (or maybe not?). I would assume that it would be better to map the buffer and directly write to GPU memory rather than any other way, so how would I achieve this?

luek baja
  • 1,475
  • 8
  • 20

1 Answers1

0

In WebGPU, if you want to map a buffer for writing (GPUMapMode.WRITE), you need to ensure that the buffer's usage flags are set appropriately. In your case, you need to include GPUBufferUsage.MAP_WRITE in the usage flags along with GPUBufferUsage.UNIFORM and GPUBufferUsage.COPY_SRC.

Here's an updated code snippet to reflect the changes:

const temp = new Float32Array([0, 0, 0, 0, 0, 0, 0, 0]);
const positionBuffer = device.createBuffer({
    size: temp.byteLength,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_SRC | GPUBufferUsage.MAP_WRITE,
    mappedAtCreation: true,
});

const positionArray = new Float32Array(positionBuffer.getMappedRange());
positionArray.set(temp);
positionBuffer.unmap();
With the updated code, you should be able to call positionBuffer.mapAsync(GPUMapMode.WRITE) successfully.

Additionally, since you're using GPUBufferUsage.COPY_SRC, it seems like you may be planning to use this buffer as a source for copying data to other buffers. If that's the case, make sure to include GPUBufferUsage.COPY_DST in the usage flags when creating the destination buffers.

const destinationBuffer = device.createBuffer({
    size: temp.byteLength,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});

By including GPUBufferUsage.COPY_DST, you can use copyBufferToBuffer or similar methods to efficiently copy data from the position buffer to the destination buffer.

Remember to adjust your shader code accordingly to use the updated buffer usage flags and ensure the appropriate binding types are used in the shader layout.

I hope this helps!

Akam
  • 1,089
  • 16
  • 24
  • Thank you. Unfortunately though, whenever I try to add the MAP_WRITE flag it throws an error saying that the only other flag allowed is COPY_SRC. More info in the question. – luek baja May 26 '23 at 21:18
  • To resolve this warning, you need to ensure that you are using the correct combination of buffer usage flags. In your case, if you want to map the buffer for writing (BufferUsage::MapWrite), you should not include the BufferUsage::Index flag. Instead, you can use BufferUsage::CopySrc if it is applicable to your specific use case. – Akam May 26 '23 at 21:42
  • 1
    It's saying that I can't use any other flag except COPY_SRC, meaning I would have to remove my UNIFORM flag which will break my program. After a few days of research I think that writing to buffers using `mapAsync` isn't a thing in WebGPU yet, or there's a specific reason it can't be implemented. Thank you for the answer though; my COPY_SRC flag should have indeed been COPY_DST. – luek baja May 26 '23 at 23:35
  • So investigating this more. Faster ways to use mapAsync with uniform buffers. One is to bunch up your uniforms. Instead of one uniform buffer per object, try one uniform buffer holding uniforms for multiple objects at different offsets. The reason is it's always faster to do less work so one call to writeBuffer for N objects is faster then N calls for N objects even if the amount of data is the same. – gman Jun 27 '23 at 08:17
  • Another is to keep your transferbuffers mapped, possibly double buffering them. So, at render time, grab an already mapped transferbuffer, copy the data to it, unmap it, issue a copyBufferToBuffer, submit your command buffer, then mapAsync the transferbuffer (and on promise put it back in your cache of transferbuffers). – gman Jun 27 '23 at 08:19