[dawn][native] Spanify QueueSubmit

Bug: 515272358
Change-Id: I53f962fad2a6867d4aa495d10945f134c9327113
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/320557
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/dawn_native.json b/src/dawn/dawn_native.json
index 841c071..2e894f7 100644
--- a/src/dawn/dawn_native.json
+++ b/src/dawn/dawn_native.json
@@ -37,7 +37,6 @@
             "BufferReadMappedRange",
             "BufferWriteMappedRange",
             "CommandEncoderWriteBuffer",
-            "QueueSubmit",
             "QueueWriteBuffer",
             "QueueWriteTexture"
         ],
diff --git a/src/dawn/native/BlitBufferToDepthStencil.cpp b/src/dawn/native/BlitBufferToDepthStencil.cpp
index 7cea29a..aa77726 100644
--- a/src/dawn/native/BlitBufferToDepthStencil.cpp
+++ b/src/dawn/native/BlitBufferToDepthStencil.cpp
@@ -540,7 +540,7 @@
     DAWN_TRY_ASSIGN(commandBuffer, commandEncoder->Finish());
 
     CommandBufferBase* commands = commandBuffer.Get();
-    device->GetQueue()->APISubmit(1, &commands);
+    device->GetQueue()->APISubmit(SpanFromRef(commands));
     return {};
 }
 
@@ -604,7 +604,7 @@
     DAWN_TRY_ASSIGN(commandBuffer, commandEncoder->Finish());
 
     CommandBufferBase* commands = commandBuffer.Get();
-    device->GetQueue()->APISubmit(1, &commands);
+    device->GetQueue()->APISubmit(SpanFromRef(commands));
     return {};
 }
 
diff --git a/src/dawn/native/CopyTextureForBrowserHelper.cpp b/src/dawn/native/CopyTextureForBrowserHelper.cpp
index 8c244b7..9ba3f2e 100644
--- a/src/dawn/native/CopyTextureForBrowserHelper.cpp
+++ b/src/dawn/native/CopyTextureForBrowserHelper.cpp
@@ -577,7 +577,7 @@
     CommandBufferBase* submitCommandBuffer = commandBuffer.Get();
 
     // Submit command buffer.
-    device->GetQueue()->APISubmit(1, &submitCommandBuffer);
+    device->GetQueue()->APISubmit(SpanFromRef(submitCommandBuffer));
     return {};
 }
 
diff --git a/src/dawn/native/Queue.cpp b/src/dawn/native/Queue.cpp
index e804891..1bc8a62 100644
--- a/src/dawn/native/Queue.cpp
+++ b/src/dawn/native/Queue.cpp
@@ -111,9 +111,7 @@
         : QueueBase(device, ObjectBase::kError, label) {}
 
   private:
-    MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override {
-        DAWN_UNREACHABLE();
-    }
+    MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) override { DAWN_UNREACHABLE(); }
     bool HasPendingCommands() const override { DAWN_UNREACHABLE(); }
     MaybeError SubmitPendingCommandsImpl() override { DAWN_UNREACHABLE(); }
     ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override { DAWN_UNREACHABLE(); }
@@ -168,17 +166,16 @@
     }
 }
 
-void QueueBase::APISubmit(uint32_t commandCount, CommandBufferBase* const* commands) {
-    MaybeError result = SubmitInternal(commandCount, commands);
+void QueueBase::APISubmit(Span<CommandBufferBase* const> commands) {
+    MaybeError result = SubmitInternal(commands);
 
     // Destroy the command buffers even if SubmitInternal failed. (crbug.com/dawn/1863)
-    for (uint32_t i = 0; i < commandCount; ++i) {
-        DAWN_UNSAFE_TODO(commands[i])->Destroy();
+    for (CommandBufferBase* commandBuffer : commands) {
+        commandBuffer->Destroy();
     }
 
-    [[maybe_unused]] bool hadError = GetDevice()->ConsumedError(
-        std::move(result), "calling %s.Submit(%s)", this,
-        DAWN_UNSAFE_TODO(ityp::span<uint32_t, CommandBufferBase* const>(commands, commandCount)));
+    [[maybe_unused]] bool hadError =
+        GetDevice()->ConsumedError(std::move(result), "calling %s.Submit(%s)", this, commands);
 }
 
 Future QueueBase::APIOnSubmittedWorkDone(const WGPUQueueWorkDoneCallbackInfo& callbackInfo) {
@@ -485,24 +482,21 @@
     return DoCopyExternalTextureForBrowser(GetDevice(), source, &destination, copySize, options);
 }
 
-MaybeError QueueBase::ValidateSubmit(uint32_t commandCount,
-                                     CommandBufferBase* const* commands,
+MaybeError QueueBase::ValidateSubmit(Span<CommandBufferBase* const> commands,
                                      BufferSet& buffersFromCommands) const {
     TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "Queue::ValidateSubmit");
     DAWN_TRY(GetDevice()->ValidateObject(this));
 
     std::set<CommandBufferBase*> uniqueCommandBuffers;
 
-    for (uint32_t i = 0; i < commandCount; ++i) {
-        DAWN_UNSAFE_TODO(DAWN_TRY(GetDevice()->ValidateObject(commands[i])));
-        DAWN_UNSAFE_TODO(DAWN_TRY(commands[i]->ValidateCanUseInSubmitNow()));
+    for (CommandBufferBase* commandBuffer : commands) {
+        DAWN_TRY(GetDevice()->ValidateObject(commandBuffer));
+        DAWN_TRY(commandBuffer->ValidateCanUseInSubmitNow());
 
-        auto insertResult = uniqueCommandBuffers.insert(DAWN_UNSAFE_TODO(commands[i]));
-        DAWN_UNSAFE_TODO(DAWN_INVALID_IF(!insertResult.second, "Submit contains duplicates of %s.",
-                                         commands[i]));
+        auto insertResult = uniqueCommandBuffers.insert(commandBuffer);
+        DAWN_INVALID_IF(!insertResult.second, "Submit contains duplicates of %s.", commandBuffer);
 
-        const CommandBufferResourceUsage& usages =
-            DAWN_UNSAFE_TODO(commands[i])->GetResourceUsages();
+        const CommandBufferResourceUsage& usages = commandBuffer->GetResourceUsages();
 
         auto ValidateBuffer = [&buffersFromCommands](BufferBase* buffer) -> MaybeError {
             if (auto [iter, inserted] = buffersFromCommands.insert(buffer); inserted) {
@@ -652,7 +646,7 @@
     return {};
 }
 
-MaybeError QueueBase::SubmitInternal(uint32_t commandCount, CommandBufferBase* const* commands) {
+MaybeError QueueBase::SubmitInternal(Span<CommandBufferBase* const> commands) {
     DeviceBase* device = GetDevice();
 
     // If device is lost, don't let any commands be submitted
@@ -669,11 +663,11 @@
     if (device->IsValidationEnabled()) {
         // TODO(crbug.com/425472913): Keep a rolling average of set size so this can reserve a
         // sufficiently large set for max of last N submits.
-        DAWN_TRY(ValidateSubmit(commandCount, commands, buffersUsedInSubmit));
+        DAWN_TRY(ValidateSubmit(commands, buffersUsedInSubmit));
     }
     DAWN_CHECK(!IsError());
 
-    DAWN_TRY(SubmitImpl(commandCount, commands));
+    DAWN_TRY(SubmitImpl(commands));
 
     // Switch the buffer state back to unmapped before Tick().
     std::move(finishUseBuffers).Invoke();
diff --git a/src/dawn/native/Queue.h b/src/dawn/native/Queue.h
index a3ea2a3..176d954 100644
--- a/src/dawn/native/Queue.h
+++ b/src/dawn/native/Queue.h
@@ -74,7 +74,7 @@
     void FormatLabel(absl::FormatSink* s) const override;
 
     // Dawn API
-    void APISubmit(uint32_t commandCount, CommandBufferBase* const* commands);
+    void APISubmit(Span<CommandBufferBase* const> commands);
     Future APIOnSubmittedWorkDone(const WGPUQueueWorkDoneCallbackInfo& callbackInfo);
     void APIWriteBuffer(BufferBase* buffer, uint64_t bufferOffset, const void* data, size_t size);
     void APIWriteTexture(const TexelCopyTextureInfo* destination,
@@ -117,7 +117,7 @@
 
     void DestroyImpl(DestroyReason reason) override;
 
-    virtual MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) = 0;
+    virtual MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) = 0;
     virtual MaybeError WriteBufferImpl(BufferBase* buffer,
                                        uint64_t bufferOffset,
                                        const void* data,
@@ -142,8 +142,7 @@
                                                      const TexelCopyTextureInfo* destination,
                                                      const Extent3D* copySize,
                                                      const CopyTextureForBrowserOptions* options);
-    MaybeError ValidateSubmit(uint32_t commandCount,
-                              CommandBufferBase* const* commands,
+    MaybeError ValidateSubmit(Span<CommandBufferBase* const> commands,
                               BufferSet& buffersFromCommands) const;
     MaybeError ValidateOnSubmittedWorkDone() const;
     MaybeError ValidateWriteTexture(const TexelCopyTextureInfo* destination,
@@ -151,7 +150,7 @@
                                     const TexelCopyBufferLayout& dataLayout,
                                     const Extent3D* writeSize) const;
 
-    MaybeError SubmitInternal(uint32_t commandCount, CommandBufferBase* const* commands);
+    MaybeError SubmitInternal(Span<CommandBufferBase* const> commands);
 
     MutexProtected<SerialMap<ExecutionSerial, std::unique_ptr<TrackTaskCallback>>> mTasksInFlight;
 };
diff --git a/src/dawn/native/d3d11/QueueD3D11.cpp b/src/dawn/native/d3d11/QueueD3D11.cpp
index b5c733e..a694047 100644
--- a/src/dawn/native/d3d11/QueueD3D11.cpp
+++ b/src/dawn/native/d3d11/QueueD3D11.cpp
@@ -314,7 +314,7 @@
     return {};
 }
 
-MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) {
+MaybeError Queue::SubmitImpl(Span<CommandBufferBase* const> commands) {
     // CommandBuffer::Execute() will modify the state of the global immediate device context, it may
     // affect following usage of it.
     // TODO(dawn:1770): figure how if we need to track and restore the state of the immediate device
@@ -323,8 +323,8 @@
     {
         auto commandContext =
             GetScopedSwapStatePendingCommandContext(QueueBase::SubmitMode::Normal);
-        for (uint32_t i = 0; i < commandCount; ++i) {
-            DAWN_TRY(ToBackend(DAWN_UNSAFE_TODO(commands[i]))->Execute(&commandContext));
+        for (CommandBufferBase* commandBuffer : commands) {
+            DAWN_TRY(ToBackend(commandBuffer)->Execute(&commandContext));
         }
     }
     DAWN_TRY(SubmitPendingCommandsImpl());
diff --git a/src/dawn/native/d3d11/QueueD3D11.h b/src/dawn/native/d3d11/QueueD3D11.h
index de4677e..3ae05d1 100644
--- a/src/dawn/native/d3d11/QueueD3D11.h
+++ b/src/dawn/native/d3d11/QueueD3D11.h
@@ -90,7 +90,7 @@
     MaybeError Initialize(bool useMonitoredFence);
     MaybeError InitializeD3DFence(bool useMonitoredFence);
 
-    MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) override;
     MaybeError WriteBufferImpl(BufferBase* buffer,
                                uint64_t bufferOffset,
                                const void* data,
diff --git a/src/dawn/native/d3d12/QueueD3D12.cpp b/src/dawn/native/d3d12/QueueD3D12.cpp
index f1a384e..e55e2fe 100644
--- a/src/dawn/native/d3d12/QueueD3D12.cpp
+++ b/src/dawn/native/d3d12/QueueD3D12.cpp
@@ -108,14 +108,14 @@
     return mD3d12SharingContract.Get();
 }
 
-MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) {
+MaybeError Queue::SubmitImpl(Span<CommandBufferBase* const> commands) {
     CommandRecordingContext* commandContext = GetPendingCommandContext();
     ExecutionSerial pendingSerial = GetPendingCommandSerial();
 
     TRACE_EVENT_BEGIN1(GetDevice()->GetPlatform(), Recording, "CommandBufferD3D12::RecordCommands",
                        "serial", uint64_t(pendingSerial));
-    for (uint32_t i = 0; i < commandCount; ++i) {
-        DAWN_TRY(ToBackend(DAWN_UNSAFE_TODO(commands[i]))->RecordCommands(commandContext));
+    for (CommandBufferBase* commandBuffer : commands) {
+        DAWN_TRY(ToBackend(commandBuffer)->RecordCommands(commandContext));
     }
     TRACE_EVENT_END1(GetDevice()->GetPlatform(), Recording, "CommandBufferD3D12::RecordCommands",
                      "serial", uint64_t(pendingSerial));
diff --git a/src/dawn/native/d3d12/QueueD3D12.h b/src/dawn/native/d3d12/QueueD3D12.h
index b4e9917..2313689 100644
--- a/src/dawn/native/d3d12/QueueD3D12.h
+++ b/src/dawn/native/d3d12/QueueD3D12.h
@@ -65,7 +65,7 @@
 
     void DestroyImpl(DestroyReason reason) override;
     MaybeError SubmitPendingCommandsImpl() override;
-    MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) override;
     bool HasPendingCommands() const override;
     ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
     void ForceEventualFlushOfCommands() override;
diff --git a/src/dawn/native/metal/QueueMTL.h b/src/dawn/native/metal/QueueMTL.h
index 54f121b..2d336df 100644
--- a/src/dawn/native/metal/QueueMTL.h
+++ b/src/dawn/native/metal/QueueMTL.h
@@ -66,7 +66,7 @@
     MaybeError Initialize();
     void UpdateCommandsScheduledEvents(ExecutionSerial scheduledSerial);
 
-    MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) override;
     bool HasPendingCommands() const override;
     MaybeError SubmitPendingCommandsImpl() override;
     ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
diff --git a/src/dawn/native/metal/QueueMTL.mm b/src/dawn/native/metal/QueueMTL.mm
index f57abdd..a600f24 100644
--- a/src/dawn/native/metal/QueueMTL.mm
+++ b/src/dawn/native/metal/QueueMTL.mm
@@ -266,13 +266,13 @@
     return SharedFence::Create(ToBackend(GetDevice()), "Internal MTLSharedEvent", &desc);
 }
 
-MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) {
+MaybeError Queue::SubmitImpl(Span<CommandBufferBase* const> commands) {
     @autoreleasepool {
         CommandRecordingContext* commandContext = GetPendingCommandContext();
 
         TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferMTL::FillCommands");
-        for (uint32_t i = 0; i < commandCount; ++i) {
-            DAWN_UNSAFE_TODO(DAWN_TRY(ToBackend(commands[i])->FillCommands(commandContext)));
+        for (CommandBufferBase* commandBuffer : commands) {
+            DAWN_TRY(ToBackend(commandBuffer)->FillCommands(commandContext));
         }
         TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferMTL::FillCommands");
 
diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp
index 862e069..6a287ff 100644
--- a/src/dawn/native/null/DeviceNull.cpp
+++ b/src/dawn/native/null/DeviceNull.cpp
@@ -444,7 +444,7 @@
 
 Queue::~Queue() {}
 
-MaybeError Queue::SubmitImpl(uint32_t, CommandBufferBase* const*) {
+MaybeError Queue::SubmitImpl(Span<CommandBufferBase* const>) {
     Device* device = ToBackend(GetDevice());
 
     DAWN_TRY(device->SubmitPendingOperations());
diff --git a/src/dawn/native/null/DeviceNull.h b/src/dawn/native/null/DeviceNull.h
index 6d8ab94..cad0758 100644
--- a/src/dawn/native/null/DeviceNull.h
+++ b/src/dawn/native/null/DeviceNull.h
@@ -297,7 +297,7 @@
 
   private:
     ~Queue() override;
-    MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) override;
     MaybeError WriteBufferImpl(BufferBase* buffer,
                                uint64_t bufferOffset,
                                const void* data,
diff --git a/src/dawn/native/opengl/QueueGL.cpp b/src/dawn/native/opengl/QueueGL.cpp
index 3857ee0..cac9221 100644
--- a/src/dawn/native/opengl/QueueGL.cpp
+++ b/src/dawn/native/opengl/QueueGL.cpp
@@ -77,17 +77,16 @@
     }
 }
 
-MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) {
+MaybeError Queue::SubmitImpl(Span<CommandBufferBase* const> commands) {
     Device* device = ToBackend(GetDevice());
-    return device->EnqueueAndFlushGL(
-        [this, commandCount, commands](const OpenGLFunctions& gl) -> MaybeError {
-            TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferGL::Execute");
-            for (uint32_t i = 0; i < commandCount; ++i) {
-                DAWN_UNSAFE_TODO(DAWN_TRY(ToBackend(commands[i])->Execute(gl)));
-            }
-            TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferGL::Execute");
-            return {};
-        });
+    return device->EnqueueAndFlushGL([this, commands](const OpenGLFunctions& gl) -> MaybeError {
+        TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferGL::Execute");
+        for (CommandBufferBase* commandBuffer : commands) {
+            DAWN_TRY(ToBackend(commandBuffer)->Execute(gl));
+        }
+        TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferGL::Execute");
+        return {};
+    });
 }
 
 MaybeError Queue::WriteBufferImpl(BufferBase* buffer,
@@ -159,7 +158,7 @@
         Ref<CommandBufferBase> commandBuffer;
         DAWN_TRY_ASSIGN(commandBuffer, commandEncoder->Finish());
         CommandBufferBase* commands = commandBuffer.Get();
-        APISubmit(1, &commands);
+        APISubmit(SpanFromRef(commands));
         return {};
     }
 
diff --git a/src/dawn/native/opengl/QueueGL.h b/src/dawn/native/opengl/QueueGL.h
index 6a7b251..159e225 100644
--- a/src/dawn/native/opengl/QueueGL.h
+++ b/src/dawn/native/opengl/QueueGL.h
@@ -55,7 +55,7 @@
   private:
     Queue(Device* device, const QueueDescriptor* descriptor);
 
-    MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) override;
     MaybeError WriteBufferImpl(BufferBase* buffer,
                                uint64_t bufferOffset,
                                const void* data,
diff --git a/src/dawn/native/vulkan/QueueVk.cpp b/src/dawn/native/vulkan/QueueVk.cpp
index 642bf58..4ce7b6e 100644
--- a/src/dawn/native/vulkan/QueueVk.cpp
+++ b/src/dawn/native/vulkan/QueueVk.cpp
@@ -99,11 +99,11 @@
     return {};
 }
 
-MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) {
+MaybeError Queue::SubmitImpl(Span<CommandBufferBase* const> commands) {
     TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferVk::RecordCommands");
     CommandRecordingContext* recordingContext = GetPendingRecordingContext();
-    for (uint32_t i = 0; i < commandCount; ++i) {
-        DAWN_UNSAFE_TODO(DAWN_TRY(ToBackend(commands[i])->RecordCommands(recordingContext)));
+    for (CommandBufferBase* commandBuffer : commands) {
+        DAWN_TRY(ToBackend(commandBuffer)->RecordCommands(recordingContext));
     }
     TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferVk::RecordCommands");
 
diff --git a/src/dawn/native/vulkan/QueueVk.h b/src/dawn/native/vulkan/QueueVk.h
index e6e2fb1..8698d4a 100644
--- a/src/dawn/native/vulkan/QueueVk.h
+++ b/src/dawn/native/vulkan/QueueVk.h
@@ -63,7 +63,7 @@
 
     MaybeError Initialize();
 
-    MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) override;
     bool HasPendingCommands() const override;
     ResultOrError<ExecutionSerial> CheckAndUpdateCompletedSerials() override;
     void ForceEventualFlushOfCommands() override;
diff --git a/src/dawn/native/webgpu/QueueWGPU.cpp b/src/dawn/native/webgpu/QueueWGPU.cpp
index 292aec2..7476365 100644
--- a/src/dawn/native/webgpu/QueueWGPU.cpp
+++ b/src/dawn/native/webgpu/QueueWGPU.cpp
@@ -30,6 +30,7 @@
 #include <limits>
 #include <vector>
 
+#include "src/dawn/common/Enumerator.h"
 #include "src/dawn/native/EventManager.h"
 #include "src/dawn/native/Instance.h"
 #include "src/dawn/native/Queue.h"
@@ -65,19 +66,18 @@
     return mSharedFence;
 }
 
-MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) {
-    if (commandCount == 0 || commands == nullptr) {
+MaybeError Queue::SubmitImpl(Span<CommandBufferBase* const> commands) {
+    if (commands.empty()) {
         return {};
     }
 
     if (IsCapturing()) {
         std::vector<schema::ObjectId> commandBufferIds;
-        commandBufferIds.reserve(commandCount);
+        commandBufferIds.reserve(commands.size());
 
-        for (uint32_t i = 0; i < commandCount; ++i) {
+        for (CommandBufferBase* commandBuffer : commands) {
             schema::ObjectId id;
-            DAWN_UNSAFE_TODO(
-                DAWN_TRY_ASSIGN(id, mCaptureContext->AddResourceAndGetId(ToBackend(commands[i]))));
+            DAWN_TRY_ASSIGN(id, mCaptureContext->AddResourceAndGetId(ToBackend(commandBuffer)));
             commandBufferIds.emplace_back(id);
         }
 
@@ -89,16 +89,16 @@
         Serialize(*mCaptureContext, cmd);
     }
 
-    std::vector<WGPUCommandBuffer> innerCommandBuffers(commandCount);
-    for (uint32_t i = 0; i < commandCount; ++i) {
-        DAWN_UNSAFE_TODO(DAWN_TRY_ASSIGN(innerCommandBuffers[i], ToBackend(commands[i])->Encode()));
+    std::vector<WGPUCommandBuffer> innerCommandBuffers(commands.size());
+    for (auto [i, commandBuffer] : Enumerate(commands)) {
+        DAWN_TRY_ASSIGN(innerCommandBuffers[i], ToBackend(commandBuffer)->Encode());
     }
 
     auto& wgpu = ToBackend(GetDevice())->wgpu.get();
-    wgpu.queueSubmit(mInnerHandle, commandCount, innerCommandBuffers.data());
+    wgpu.queueSubmit(mInnerHandle, innerCommandBuffers.size(), innerCommandBuffers.data());
 
-    for (uint32_t i = 0; i < commandCount; ++i) {
-        wgpu.commandBufferRelease(innerCommandBuffers[i]);
+    for (WGPUCommandBuffer commandBuffer : innerCommandBuffers) {
+        wgpu.commandBufferRelease(commandBuffer);
     }
 
     DAWN_TRY(SubmitFutureSync());
diff --git a/src/dawn/native/webgpu/QueueWGPU.h b/src/dawn/native/webgpu/QueueWGPU.h
index def95c0..1cffffa 100644
--- a/src/dawn/native/webgpu/QueueWGPU.h
+++ b/src/dawn/native/webgpu/QueueWGPU.h
@@ -54,7 +54,7 @@
 
   private:
     Queue(Device* device, const QueueDescriptor* descriptor);
-    MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override;
+    MaybeError SubmitImpl(Span<CommandBufferBase* const> commands) override;
     MaybeError WriteBufferImpl(BufferBase* buffer,
                                uint64_t bufferOffset,
                                const void* data,
diff --git a/src/dawn/tests/unittests/native/mocks/QueueMock.cpp b/src/dawn/tests/unittests/native/mocks/QueueMock.cpp
index c0c1822..3515eb2 100644
--- a/src/dawn/tests/unittests/native/mocks/QueueMock.cpp
+++ b/src/dawn/tests/unittests/native/mocks/QueueMock.cpp
@@ -38,11 +38,10 @@
     ON_CALL(*this, DestroyImpl).WillByDefault([this](DestroyReason reason) {
         this->QueueBase::DestroyImpl(reason);
     });
-    ON_CALL(*this, SubmitImpl)
-        .WillByDefault([this](uint32_t, CommandBufferBase* const*) -> MaybeError {
-            this->QueueBase::IncrementLastSubmittedCommandSerial();
-            return {};
-        });
+    ON_CALL(*this, SubmitImpl).WillByDefault([this](Span<CommandBufferBase* const>) -> MaybeError {
+        this->QueueBase::IncrementLastSubmittedCommandSerial();
+        return {};
+    });
     ON_CALL(*this, CheckAndUpdateCompletedSerials)
         .WillByDefault([this]() -> ResultOrError<ExecutionSerial> {
             return this->QueueBase::GetLastSubmittedCommandSerial();
diff --git a/src/dawn/tests/unittests/native/mocks/QueueMock.h b/src/dawn/tests/unittests/native/mocks/QueueMock.h
index 6c669b8..d7781cf 100644
--- a/src/dawn/tests/unittests/native/mocks/QueueMock.h
+++ b/src/dawn/tests/unittests/native/mocks/QueueMock.h
@@ -40,7 +40,7 @@
     QueueMock(DeviceMock* device, const QueueDescriptor* descriptor);
     ~QueueMock() override;
 
-    MOCK_METHOD(MaybeError, SubmitImpl, (uint32_t, CommandBufferBase* const*), (override));
+    MOCK_METHOD(MaybeError, SubmitImpl, (Span<CommandBufferBase* const>), (override));
     MOCK_METHOD(MaybeError,
                 WriteBufferImpl,
                 (BufferBase*, uint64_t, const void*, size_t),