When can I release SDL_GPUTransferBuffer?

TL;DR: Is releasing a transfer buffer before submitting the corresponding copy pass and command buffer an appropriate way to use the API? Or does this open me up to potential issues?

I’m throwing together a project in SDL3 (mostly for fun) and wanted to check that I’m doing this right.
Currently, to upload data to buffers, I’m creating a copy pass and then using a function that takes in that copy pass. The function creates a transfer buffer, maps it, copies the data over, unmaps it, and then uses the copy pass to upload the data to my SDL_GPUBuffer. Immediately after calling SDL_UploadToGPUBuffer, I’m calling SDL_ReleaseGPUTransferBuffer. I assume that releasing waits until the copy pass is ended and the command buffer is submitted before deleting the data, but I just wanted to make sure this is an appropriate (if simple) way to use the API. It’s working fine currently, but I don’t want to run into weird conditions where it decides to delete the transfer buffer early.

Relevant source code

In my main code:

SDL_GPUCommandBuffer *copy_command_buffer = SDL_AcquireGPUCommandBuffer(device);

SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(copy_command_buffer);

upload_to_buffer(device, copy_pass, vertex_buffer, vertices, sizeof(vertices));
upload_to_buffer(device, copy_pass, index_buffer, indices, sizeof(indices));

SDL_EndGPUCopyPass(copy_pass);

SDL_SubmitGPUCommandBuffer(copy_command_buffer);

Contents of upload_to_buffer():

void upload_to_buffer(SDL_GPUDevice *device, SDL_GPUCopyPass *copy_pass, SDL_GPUBuffer *dest_buffer, void *data, size_t size) {
    SDL_GPUTransferBufferCreateInfo transfer_buffer_ci = {
        .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
        .size = size,
    };

    SDL_GPUTransferBuffer *transfer_buffer = SDL_CreateGPUTransferBuffer(device, &transfer_buffer_ci);
    void *dest = SDL_MapGPUTransferBuffer(device, transfer_buffer, false);
    mempcpy(dest, data, size);
    SDL_UnmapGPUTransferBuffer(device, transfer_buffer);
    SDL_GPUTransferBufferLocation source_location = {
        .transfer_buffer = transfer_buffer,
        .offset = 0,
    };
    SDL_GPUBufferRegion dest_region = {
        .buffer = dest_buffer,
        .offset = 0,
        .size = size,
    };
    SDL_UploadToGPUBuffer(copy_pass, &source_location, &dest_region, false);
    SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
}

Yes, the transfer buffer is reference counted, so once you’ve scheduled a transfer the command buffer retains a reference to it and you can release your own reference to it.

1 Like

Question: are you calling upload_to_buffer every frame? Because I have a similar doubt about it

I am not. However, I assume based on my knowledge that you can. In fact, the documentation for command buffers recommends using 2 command buffers per frame, one with all your copy commands and the other with all your render commands. Just submit the copy command buffer first.

I’m pretty sure that in this case, you may want to save your transfer buffer to save yourself from creating and destroying one every frame. Just make sure to enable cycling if you want to prevent the risk of overwriting a buffer that’s being used in a previous frame. That being said, the documentation for creating transfer buffers only mentions this being a particular performance saver for download buffers, not upload buffers. So ymmv and it’s worth profiling.

Yes, I’ve figured it out later, since you are setting cycle to false. Thanks!