Implement queue-related futures on Vulkan
Bug: dawn:2056
Change-Id: Ida58db0c2a9d911e58e13e927252090b3e24e4ef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/156700
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn/native/vulkan/QueueVk.cpp b/src/dawn/native/vulkan/QueueVk.cpp
index ede2519..20ef690 100644
--- a/src/dawn/native/vulkan/QueueVk.cpp
+++ b/src/dawn/native/vulkan/QueueVk.cpp
@@ -140,31 +140,32 @@
ResultOrError<ExecutionSerial> Queue::CheckAndUpdateCompletedSerials() {
Device* device = ToBackend(GetDevice());
+ return mFencesInFlight.Use([&](auto fencesInFlight) -> ResultOrError<ExecutionSerial> {
+ ExecutionSerial fenceSerial(0);
+ while (!fencesInFlight->empty()) {
+ VkFence fence = fencesInFlight->front().first;
+ ExecutionSerial tentativeSerial = fencesInFlight->front().second;
+ VkResult result = VkResult::WrapUnsafe(INJECT_ERROR_OR_RUN(
+ device->fn.GetFenceStatus(device->GetVkDevice(), fence), VK_ERROR_DEVICE_LOST));
- ExecutionSerial fenceSerial(0);
- while (!mFencesInFlight.empty()) {
- VkFence fence = mFencesInFlight.front().first;
- ExecutionSerial tentativeSerial = mFencesInFlight.front().second;
- VkResult result = VkResult::WrapUnsafe(INJECT_ERROR_OR_RUN(
- device->fn.GetFenceStatus(device->GetVkDevice(), fence), VK_ERROR_DEVICE_LOST));
+ // Fence are added in order, so we can stop searching as soon
+ // as we see one that's not ready.
+ if (result == VK_NOT_READY) {
+ return fenceSerial;
+ } else {
+ DAWN_TRY(CheckVkSuccess(::VkResult(result), "GetFenceStatus"));
+ }
- // Fence are added in order, so we can stop searching as soon
- // as we see one that's not ready.
- if (result == VK_NOT_READY) {
- return fenceSerial;
- } else {
- DAWN_TRY(CheckVkSuccess(::VkResult(result), "GetFenceStatus"));
+ // Update fenceSerial since fence is ready.
+ fenceSerial = tentativeSerial;
+
+ mUnusedFences.push_back(fence);
+
+ DAWN_ASSERT(fenceSerial > GetCompletedCommandSerial());
+ fencesInFlight->pop_front();
}
-
- // Update fenceSerial since fence is ready.
- fenceSerial = tentativeSerial;
-
- mUnusedFences.push_back(fence);
-
- DAWN_ASSERT(fenceSerial > GetCompletedCommandSerial());
- mFencesInFlight.pop();
- }
- return fenceSerial;
+ return fenceSerial;
+ });
}
void Queue::ForceEventualFlushOfCommands() {
@@ -192,35 +193,37 @@
DAWN_UNUSED(waitIdleResult);
// Make sure all fences are complete by explicitly waiting on them all
- while (!mFencesInFlight.empty()) {
- VkFence fence = mFencesInFlight.front().first;
- ExecutionSerial fenceSerial = mFencesInFlight.front().second;
- DAWN_ASSERT(fenceSerial > GetCompletedCommandSerial());
+ mFencesInFlight.Use([&](auto fencesInFlight) {
+ while (!fencesInFlight->empty()) {
+ VkFence fence = fencesInFlight->front().first;
+ ExecutionSerial fenceSerial = fencesInFlight->front().second;
+ DAWN_ASSERT(fenceSerial > GetCompletedCommandSerial());
- VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT);
- do {
- // If WaitForIdleForDesctruction is called while we are Disconnected, it means that
- // the device lost came from the ErrorInjector and we need to wait without allowing
- // any more error to be injected. This is because the device lost was "fake" and
- // commands might still be running.
- if (GetDevice()->GetState() == Device::State::Disconnected) {
- result = VkResult::WrapUnsafe(
- device->fn.WaitForFences(vkDevice, 1, &*fence, true, UINT64_MAX));
- continue;
- }
+ VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT);
+ do {
+ // If WaitForIdleForDesctruction is called while we are Disconnected, it means that
+ // the device lost came from the ErrorInjector and we need to wait without allowing
+ // any more error to be injected. This is because the device lost was "fake" and
+ // commands might still be running.
+ if (GetDevice()->GetState() == Device::State::Disconnected) {
+ result = VkResult::WrapUnsafe(
+ device->fn.WaitForFences(vkDevice, 1, &*fence, true, UINT64_MAX));
+ continue;
+ }
- result = VkResult::WrapUnsafe(INJECT_ERROR_OR_RUN(
- device->fn.WaitForFences(vkDevice, 1, &*fence, true, UINT64_MAX),
- VK_ERROR_DEVICE_LOST));
- } while (result == VK_TIMEOUT);
- // Ignore errors from vkWaitForFences: it can be either OOM which we can't do anything
- // about (and we need to keep going with the destruction of all fences), or device
- // loss, which means the workload on the GPU is no longer accessible and we can
- // safely destroy the fence.
+ result = VkResult::WrapUnsafe(INJECT_ERROR_OR_RUN(
+ device->fn.WaitForFences(vkDevice, 1, &*fence, true, UINT64_MAX),
+ VK_ERROR_DEVICE_LOST));
+ } while (result == VK_TIMEOUT);
+ // Ignore errors from vkWaitForFences: it can be either OOM which we can't do anything
+ // about (and we need to keep going with the destruction of all fences), or device
+ // loss, which means the workload on the GPU is no longer accessible and we can
+ // safely destroy the fence.
- device->fn.DestroyFence(vkDevice, fence, nullptr);
- mFencesInFlight.pop();
- }
+ device->fn.DestroyFence(vkDevice, fence, nullptr);
+ fencesInFlight->pop_front();
+ }
+ });
return {};
}
@@ -398,7 +401,7 @@
}
IncrementLastSubmittedCommandSerial();
ExecutionSerial lastSubmittedSerial = GetLastSubmittedCommandSerial();
- mFencesInFlight.emplace(fence, lastSubmittedSerial);
+ mFencesInFlight->emplace_back(fence, lastSubmittedSerial);
for (size_t i = 0; i < mRecordingContext.commandBufferList.size(); ++i) {
CommandPoolAndBuffer submittedCommands = {mRecordingContext.commandPoolList[i],
@@ -479,10 +482,12 @@
// Some fences might still be marked as in-flight if we shut down because of a device loss.
// Delete them since at this point all commands are complete.
- while (!mFencesInFlight.empty()) {
- device->fn.DestroyFence(vkDevice, *mFencesInFlight.front().first, nullptr);
- mFencesInFlight.pop();
- }
+ mFencesInFlight.Use([&](auto fencesInFlight) {
+ while (!fencesInFlight->empty()) {
+ device->fn.DestroyFence(vkDevice, *fencesInFlight->front().first, nullptr);
+ fencesInFlight->pop_front();
+ }
+ });
for (VkFence fence : mUnusedFences) {
device->fn.DestroyFence(vkDevice, fence, nullptr);
@@ -492,4 +497,35 @@
QueueBase::DestroyImpl();
}
+ResultOrError<bool> Queue::WaitForQueueSerial(ExecutionSerial serial, Nanoseconds timeout) {
+ Device* device = ToBackend(GetDevice());
+ VkDevice vkDevice = device->GetVkDevice();
+ VkResult waitResult = mFencesInFlight.Use([&](auto fencesInFlight) {
+ // Search from for the first fence >= serial.
+ VkFence waitFence = VK_NULL_HANDLE;
+ for (auto it = fencesInFlight->begin(); it != fencesInFlight->end(); ++it) {
+ if (it->second >= serial) {
+ waitFence = it->first;
+ break;
+ }
+ }
+ if (waitFence == VK_NULL_HANDLE) {
+ // Fence not found. This serial must have already completed.
+ // Return a VK_SUCCESS status.
+ DAWN_ASSERT(serial <= GetCompletedCommandSerial());
+ return VkResult::WrapUnsafe(VK_SUCCESS);
+ }
+ // Wait for the fence.
+ return VkResult::WrapUnsafe(
+ INJECT_ERROR_OR_RUN(device->fn.WaitForFences(vkDevice, 1, &*waitFence, true,
+ static_cast<uint64_t>(timeout)),
+ VK_ERROR_DEVICE_LOST));
+ });
+ if (waitResult == VK_TIMEOUT) {
+ return false;
+ }
+ DAWN_TRY(CheckVkSuccess(::VkResult(waitResult), "vkWaitForFences"));
+ return true;
+}
+
} // namespace dawn::native::vulkan
diff --git a/src/dawn/native/vulkan/QueueVk.h b/src/dawn/native/vulkan/QueueVk.h
index d3bd704..a4b5b22 100644
--- a/src/dawn/native/vulkan/QueueVk.h
+++ b/src/dawn/native/vulkan/QueueVk.h
@@ -28,7 +28,7 @@
#ifndef SRC_DAWN_NATIVE_VULKAN_QUEUEVK_H_
#define SRC_DAWN_NATIVE_VULKAN_QUEUEVK_H_
-#include <queue>
+#include <deque>
#include <utility>
#include <vector>
@@ -57,6 +57,8 @@
void RecycleCompletedCommands(ExecutionSerial completedSerial);
+ ResultOrError<bool> WaitForQueueSerial(ExecutionSerial serial, Nanoseconds timeout) override;
+
private:
Queue(Device* device, const QueueDescriptor* descriptor, uint32_t family);
~Queue() override;
@@ -80,7 +82,7 @@
// This works only because we have a single queue. Each submit to a queue is associated
// to a serial and a fence, such that when the fence is "ready" we know the operations
// have finished.
- std::queue<std::pair<VkFence, ExecutionSerial>> mFencesInFlight;
+ MutexProtected<std::deque<std::pair<VkFence, ExecutionSerial>>> mFencesInFlight;
// Fences in the unused list aren't reset yet.
std::vector<VkFence> mUnusedFences;
diff --git a/src/dawn/tests/end2end/BufferTests.cpp b/src/dawn/tests/end2end/BufferTests.cpp
index 76c20ff..40bfa69 100644
--- a/src/dawn/tests/end2end/BufferTests.cpp
+++ b/src/dawn/tests/end2end/BufferTests.cpp
@@ -668,7 +668,7 @@
DAWN_INSTANTIATE_PREFIXED_TEST_P(Future,
BufferMappingTests,
- {MetalBackend()},
+ {MetalBackend(), VulkanBackend()},
std::initializer_list<std::optional<wgpu::CallbackMode>>{
wgpu::CallbackMode::WaitAnyOnly,
wgpu::CallbackMode::AllowProcessEvents,
@@ -880,7 +880,7 @@
DAWN_INSTANTIATE_PREFIXED_TEST_P(Future,
BufferMappingCallbackTests,
- {MetalBackend()},
+ {MetalBackend(), VulkanBackend()},
std::initializer_list<std::optional<wgpu::CallbackMode>>{
wgpu::CallbackMode::WaitAnyOnly,
wgpu::CallbackMode::AllowProcessEvents,
diff --git a/src/dawn/tests/end2end/EventTests.cpp b/src/dawn/tests/end2end/EventTests.cpp
index 073b2cf..b4e5141 100644
--- a/src/dawn/tests/end2end/EventTests.cpp
+++ b/src/dawn/tests/end2end/EventTests.cpp
@@ -460,7 +460,7 @@
DAWN_INSTANTIATE_TEST_P(EventCompletionTests,
// TODO(crbug.com/dawn/2058): Enable tests for the rest of the backends.
- {MetalBackend()},
+ {MetalBackend(), VulkanBackend()},
{
WaitTypeAndCallbackMode::TimedWaitAny_WaitAnyOnly,
WaitTypeAndCallbackMode::TimedWaitAny_AllowSpontaneous,
@@ -576,7 +576,8 @@
DAWN_INSTANTIATE_TEST(WaitAnyTests,
// TODO(crbug.com/dawn/2058): Enable tests for the rest of the backends.
- MetalBackend());
+ MetalBackend(),
+ VulkanBackend());
} // anonymous namespace
} // namespace dawn