`[CommandBuffer] cannot be submitted more than once` when reusing `WGpuCommandBuffer`
Closed this issue · 4 comments
I wrote a compute shader that takes 2 input buffers and writes 1 output buffer. I'm trying to periodically execute the same computation by setting only the contents of the buffers and resubmitting the same, pre-encoded commands.
The documentation of wgpu_queue_submit_one()
states that the submitted command buffer is held alive for later resubmission, but when I call wgpu_queue_submit_one()
again on the same queue with the same command buffer, I get this console warning:
[CommandBuffer] cannot be submitted more than once.
- While calling [Queue].Submit([[CommandBuffer]])
Moreover, calling wgpu_buffer_read_mapped_range()
on the output buffer returns the contents of the previous computation.
This is how I initialize all the required objects (they are all globally declared in this example):
const auto* shader2 = R"( /* Shader code */)"
WGpuShaderModuleDescriptor shaderModuleDescriptor{.code = shader2};
shaderModule = wgpu_device_create_shader_module(device, &shaderModuleDescriptor);
pipeline = wgpu_device_create_compute_pipeline(device, shaderModule, "main", 0, nullptr, 0);
queue = wgpu_device_get_queue(device);
// BindGroupEntries creation...
bindGroupLayout = wgpu_pipeline_get_bind_group_layout(pipeline, 0);
bindGroup = wgpu_device_create_bind_group(device, bindGroupLayout, bindGroupEntries.data(), bindGroupEntries.size());
// 2 input buffers, 1 output buffer and 1 staging output buffer creation...
commandEncoder = wgpu_device_create_command_encoder_simple(device);
WGpuComputePassDescriptor computePassDescriptor = WGPU_COMPUTE_PASS_DESCRIPTOR_DEFAULT_INITIALIZER;
computePassEncoder = wgpu_command_encoder_begin_compute_pass(commandEncoder, &computePassDescriptor);
wgpu_encoder_set_pipeline(computePassEncoder, pipeline);
wgpu_encoder_set_bind_group(computePassEncoder, 0, bindGroup);
wgpu_compute_pass_encoder_dispatch_workgroups_indirect(computePassEncoder, workgroupsIndirectBuffer, 0);
wgpu_encoder_end(computePassEncoder);
wgpu_command_encoder_copy_buffer_to_buffer(commandEncoder, outputBuffer, 0, outputStagingBuffer, 0,
wgpu_buffer_size(outputBuffer));
commandBuffer = wgpu_encoder_finish(commandEncoder);
then I periodically call my compute()
function that sets the contents of the 2 input buffers and resubmits commandBuffer
void compute(/* ... */) {
// wgpu_queue_write_buffer() calls on 2 input buffers....
wgpu_queue_submit_one(queue, commandBuffer);
// Reading and unmapping staging output buffer...
}
Thanks for the report!
It looks like the spec changed on 30th of June 2022 to disallow resubmitting GPUCommandBuffers: https://github.com/gpuweb/gpuweb/pull/3116/files#diff-40cc3a1ba233cc3ca7b6d5873260da9676f6ae20bb897b62f7871c80d0bda4e9R7705-R7706
Or rather, the PR says
This has always been true, but it turns out we were missing an actual description of this from the validation in the spec.
so it looks like I had not known that the intent was for GPUCommandBuffers to be single-use when I originally created the bindings code.
After the above PR, the spec clearly says
https://www.w3.org/TR/webgpu/#dom-gpuqueue-submit: Submitted command buffers cannot be used again.
https://www.w3.org/TR/webgpu/#command-buffers: A [GPUCommandBuffer](https://www.w3.org/TR/webgpu/#gpucommandbuffer) can only be submitted once, at which point it becomes [invalid](https://www.w3.org/TR/webgpu/#invalid). To reuse rendering commands across multiple submissions, use [GPURenderBundle](https://www.w3.org/TR/webgpu/#gpurenderbundle).
I've pushed a fix in the above commit. (the fix being to delete these functions). Looks like the WebGPU specification wants one to use render bundles when wanting to persist commands for later resubmission.
That's a bummer, I was trying to minimize the setup operations at every computation call and I'd rather not rebuild everything starting from the commandEncoder. Since we are here, do you know if there's something similar to GPURenderBundle but for compute pipelines?
That is a good question. I believe there isn't anything, probably since the presumption is that compute dispatches are simpler than render dispatches? @kainino0x would definitely know the rationale: is there anything similar to GPUComputeBundle?
There is nothing similar to GPUComputeBundle. The restrictions of GPURenderBundle are based on underlying D3D12/Metal/Vulkan APIs. If my reading of old investigations is correct, it's basically because Metal doesn't have any such thing:
gpuweb/gpuweb#301
Reusable command buffers and/or more generic bundles are still on the table, but won't come soon:
gpuweb/gpuweb#4138