Mesa (main): lavapipe: rework queue to use u_queue
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Thu Jul 29 00:15:31 UTC 2021
Module: Mesa
Branch: main
Commit: 032d4a0e7d5f43c7cbdd5e11af0ebdce4de85ae6
URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=032d4a0e7d5f43c7cbdd5e11af0ebdce4de85ae6
Author: Mike Blumenkrantz <michael.blumenkrantz at gmail.com>
Date: Fri Jul 23 10:34:10 2021 -0400
lavapipe: rework queue to use u_queue
this simplifies the entire queue mechanism and makes it more consistent:
previously some cases (e.g., null cmdbuf submission) would immediately
be marked as finished, which meant that fences could theoretically become
desynchronized, breaking application assumptions
Reviewed-by: Dave Airlie <airlied at redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12071>
---
src/gallium/frontends/lavapipe/lvp_device.c | 259 ++++++++++++++-------------
src/gallium/frontends/lavapipe/lvp_private.h | 18 +-
src/gallium/frontends/lavapipe/lvp_wsi.c | 2 +-
3 files changed, 146 insertions(+), 133 deletions(-)
diff --git a/src/gallium/frontends/lavapipe/lvp_device.c b/src/gallium/frontends/lavapipe/lvp_device.c
index 895ba87a377..2c40042fb92 100644
--- a/src/gallium/frontends/lavapipe/lvp_device.c
+++ b/src/gallium/frontends/lavapipe/lvp_device.c
@@ -1121,45 +1121,43 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(
return vk_instance_get_physical_device_proc_addr(&instance->vk, pName);
}
-static int queue_thread(void *data)
+static void
+set_last_fence(struct lvp_device *device, struct pipe_fence_handle *handle)
{
- struct lvp_queue *queue = data;
-
- mtx_lock(&queue->m);
- while (!queue->shutdown) {
- struct lvp_queue_work *task;
- while (list_is_empty(&queue->workqueue) && !queue->shutdown)
- cnd_wait(&queue->new_work, &queue->m);
-
- if (queue->shutdown)
- break;
+ simple_mtx_lock(&device->queue.last_lock);
+ device->pscreen->fence_reference(device->pscreen, &device->queue.last_fence, handle);
+ simple_mtx_unlock(&device->queue.last_lock);
+}
- task = list_first_entry(&queue->workqueue, struct lvp_queue_work,
- list);
+void
+queue_thread_noop(void *data, void *gdata, int thread_index)
+{
+ struct lvp_device *device = gdata;
+ struct lvp_fence *fence = data;
+ struct pipe_fence_handle *handle = NULL;
+ device->queue.ctx->flush(device->queue.ctx, &handle, 0);
+ fence->handle = handle;
+ set_last_fence(device, handle);
+}
- mtx_unlock(&queue->m);
- //execute
- for (unsigned i = 0; i < task->cmd_buffer_count; i++) {
- lvp_execute_cmds(queue->device, queue, task->cmd_buffers[i]);
- }
+static void
+queue_thread(void *data, void *gdata, int thread_index)
+{
+ struct lvp_queue_work *task = data;
+ struct lvp_device *device = gdata;
+ struct lvp_queue *queue = &device->queue;
- if (task->cmd_buffer_count) {
- struct pipe_fence_handle *handle = NULL;
- queue->ctx->flush(queue->ctx, task->fence ? &handle : NULL, 0);
- if (task->fence) {
- mtx_lock(&queue->device->fence_lock);
- task->fence->handle = handle;
- mtx_unlock(&queue->device->fence_lock);
- }
- } else if (task->fence)
- task->fence->signaled = true;
- p_atomic_dec(&queue->count);
- mtx_lock(&queue->m);
- list_del(&task->list);
- free(task);
+ //execute
+ for (unsigned i = 0; i < task->cmd_buffer_count; i++) {
+ lvp_execute_cmds(queue->device, queue, task->cmd_buffers[i]);
}
- mtx_unlock(&queue->m);
- return 0;
+
+ struct pipe_fence_handle *handle = NULL;
+ queue->ctx->flush(queue->ctx, &handle, 0);
+ if (task->fence)
+ task->fence->handle = handle;
+ set_last_fence(device, handle);
+ free(task);
}
static VkResult
@@ -1167,13 +1165,13 @@ lvp_queue_init(struct lvp_device *device, struct lvp_queue *queue)
{
queue->device = device;
+ simple_mtx_init(&queue->last_lock, mtx_plain);
queue->flags = 0;
+ queue->timeline = 0;
queue->ctx = device->pscreen->context_create(device->pscreen, NULL, PIPE_CONTEXT_ROBUST_BUFFER_ACCESS);
queue->cso = cso_create_context(queue->ctx, CSO_NO_VBUF);
- list_inithead(&queue->workqueue);
+ util_queue_init(&queue->queue, "lavapipe", 8, 1, UTIL_QUEUE_INIT_RESIZE_IF_FULL, device);
p_atomic_set(&queue->count, 0);
- mtx_init(&queue->m, mtx_plain);
- queue->exec_thread = u_thread_create(queue_thread, queue);
vk_object_base_init(&device->vk, &queue->base, VK_OBJECT_TYPE_QUEUE);
return VK_SUCCESS;
@@ -1182,17 +1180,12 @@ lvp_queue_init(struct lvp_device *device, struct lvp_queue *queue)
static void
lvp_queue_finish(struct lvp_queue *queue)
{
- mtx_lock(&queue->m);
- queue->shutdown = true;
- cnd_broadcast(&queue->new_work);
- mtx_unlock(&queue->m);
-
- thrd_join(queue->exec_thread, NULL);
+ util_queue_finish(&queue->queue);
+ util_queue_destroy(&queue->queue);
- cnd_destroy(&queue->new_work);
- mtx_destroy(&queue->m);
cso_destroy_context(queue->cso);
queue->ctx->destroy(queue->ctx);
+ simple_mtx_destroy(&queue->last_lock);
}
VKAPI_ATTR VkResult VKAPI_CALL lvp_CreateDevice(
@@ -1243,7 +1236,6 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_CreateDevice(
device->instance = (struct lvp_instance *)physical_device->vk.instance;
device->physical_device = physical_device;
- mtx_init(&device->fence_lock, mtx_plain);
device->pscreen = physical_device->pscreen;
lvp_queue_init(device, &device->queue);
@@ -1260,6 +1252,8 @@ VKAPI_ATTR void VKAPI_CALL lvp_DestroyDevice(
{
LVP_FROM_HANDLE(lvp_device, device, _device);
+ if (device->queue.last_fence)
+ device->pscreen->fence_reference(device->pscreen, &device->queue.last_fence, NULL);
lvp_queue_finish(&device->queue);
vk_device_finish(&device->vk);
vk_free(&device->vk.alloc, device);
@@ -1355,43 +1349,36 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_QueueSubmit(
LVP_FROM_HANDLE(lvp_queue, queue, _queue);
LVP_FROM_HANDLE(lvp_fence, fence, _fence);
+ if (fence)
+ fence->timeline = p_atomic_inc_return(&queue->timeline);
+
if (submitCount == 0)
goto just_signal_fence;
- for (uint32_t i = 0; i < submitCount; i++) {
- uint32_t task_size = sizeof(struct lvp_queue_work) + pSubmits[i].commandBufferCount * sizeof(struct lvp_cmd_buffer *);
- struct lvp_queue_work *task = malloc(task_size);
- task->cmd_buffer_count = pSubmits[i].commandBufferCount;
- task->fence = fence;
- task->cmd_buffers = (struct lvp_cmd_buffer **)(task + 1);
+ /* - calculate cmdbuf count
+ * - create task for enqueuing cmdbufs
+ * - enqueue job
+ */
+ uint32_t cmdbuf_count = 0;
+ for (uint32_t i = 0; i < submitCount; i++)
+ cmdbuf_count += pSubmits[i].commandBufferCount;
+
+ struct lvp_queue_work *task = malloc(sizeof(struct lvp_queue_work) + cmdbuf_count * sizeof(struct lvp_cmd_buffer *));
+ task->cmd_buffer_count = cmdbuf_count;
+ task->fence = fence;
+ task->cmd_buffers = (struct lvp_cmd_buffer **)(task + 1);
+ unsigned c = 0;
+ for (uint32_t i = 0; i < submitCount; i++) {
for (uint32_t j = 0; j < pSubmits[i].commandBufferCount; j++) {
- task->cmd_buffers[j] = lvp_cmd_buffer_from_handle(pSubmits[i].pCommandBuffers[j]);
+ task->cmd_buffers[c++] = lvp_cmd_buffer_from_handle(pSubmits[i].pCommandBuffers[j]);
}
-
- mtx_lock(&queue->m);
- p_atomic_inc(&queue->count);
- list_addtail(&task->list, &queue->workqueue);
- cnd_signal(&queue->new_work);
- mtx_unlock(&queue->m);
}
+
+ util_queue_add_job(&queue->queue, task, fence ? &fence->fence : NULL, queue_thread, NULL, 0);
return VK_SUCCESS;
just_signal_fence:
- fence->signaled = true;
- return VK_SUCCESS;
-}
-
-static VkResult queue_wait_idle(struct lvp_queue *queue, uint64_t timeout)
-{
- if (timeout == 0)
- return p_atomic_read(&queue->count) == 0 ? VK_SUCCESS : VK_TIMEOUT;
- if (timeout == UINT64_MAX)
- while (p_atomic_read(&queue->count))
- os_time_sleep(100);
- else {
- int64_t atime = os_time_get_absolute_timeout(timeout);
- if (!os_wait_until_zero_abs_timeout(&queue->count, atime))
- return VK_TIMEOUT;
- }
+ if (fence)
+ util_queue_add_job(&queue->queue, fence, &fence->fence, queue_thread_noop, NULL, 0);
return VK_SUCCESS;
}
@@ -1400,7 +1387,12 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_QueueWaitIdle(
{
LVP_FROM_HANDLE(lvp_queue, queue, _queue);
- return queue_wait_idle(queue, UINT64_MAX);
+ util_queue_finish(&queue->queue);
+ simple_mtx_lock(&queue->last_lock);
+ if (queue->last_fence)
+ queue->device->pscreen->fence_finish(queue->device->pscreen, NULL, queue->last_fence, PIPE_TIMEOUT_INFINITE);
+ simple_mtx_unlock(&queue->last_lock);
+ return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL lvp_DeviceWaitIdle(
@@ -1408,7 +1400,12 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_DeviceWaitIdle(
{
LVP_FROM_HANDLE(lvp_device, device, _device);
- return queue_wait_idle(&device->queue, UINT64_MAX);
+ util_queue_finish(&device->queue.queue);
+ simple_mtx_lock(&device->queue.last_lock);
+ if (device->queue.last_fence)
+ device->pscreen->fence_finish(device->pscreen, NULL, device->queue.last_fence, PIPE_TIMEOUT_INFINITE);
+ simple_mtx_unlock(&device->queue.last_lock);
+ return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL lvp_AllocateMemory(
@@ -1712,11 +1709,12 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_CreateFence(
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (fence == NULL)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
-
vk_object_base_init(&device->vk, &fence->base, VK_OBJECT_TYPE_FENCE);
- fence->signaled = pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT;
+ util_queue_fence_init(&fence->fence);
+ fence->signalled = (pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT) == VK_FENCE_CREATE_SIGNALED_BIT;
fence->handle = NULL;
+ fence->timeline = 0;
*pFence = lvp_fence_to_handle(fence);
return VK_SUCCESS;
@@ -1732,6 +1730,9 @@ VKAPI_ATTR void VKAPI_CALL lvp_DestroyFence(
if (!_fence)
return;
+ /* evade annoying destroy assert */
+ util_queue_fence_init(&fence->fence);
+ util_queue_fence_destroy(&fence->fence);
if (fence->handle)
device->pscreen->fence_reference(device->pscreen, &fence->handle, NULL);
@@ -1747,13 +1748,19 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_ResetFences(
LVP_FROM_HANDLE(lvp_device, device, _device);
for (unsigned i = 0; i < fenceCount; i++) {
struct lvp_fence *fence = lvp_fence_from_handle(pFences[i]);
+ /* ensure u_queue doesn't explode when submitting a completed lvp_fence
+ * which has not yet signalled its u_queue fence
+ */
+ util_queue_fence_wait(&fence->fence);
- fence->signaled = false;
-
- mtx_lock(&device->fence_lock);
- if (fence->handle)
+ if (fence->handle) {
+ simple_mtx_lock(&device->queue.last_lock);
+ if (fence->handle == device->queue.last_fence)
+ device->pscreen->fence_reference(device->pscreen, &device->queue.last_fence, NULL);
+ simple_mtx_unlock(&device->queue.last_lock);
device->pscreen->fence_reference(device->pscreen, &fence->handle, NULL);
- mtx_unlock(&device->fence_lock);
+ }
+ fence->signalled = false;
}
return VK_SUCCESS;
}
@@ -1765,25 +1772,16 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_GetFenceStatus(
LVP_FROM_HANDLE(lvp_device, device, _device);
LVP_FROM_HANDLE(lvp_fence, fence, _fence);
- if (fence->signaled)
+ if (fence->signalled)
return VK_SUCCESS;
- mtx_lock(&device->fence_lock);
-
- if (!fence->handle) {
- mtx_unlock(&device->fence_lock);
+ if (!util_queue_fence_is_signalled(&fence->fence) ||
+ !fence->handle ||
+ !device->pscreen->fence_finish(device->pscreen, NULL, fence->handle, 0))
return VK_NOT_READY;
- }
- bool signalled = device->pscreen->fence_finish(device->pscreen,
- NULL,
- fence->handle,
- 0);
- mtx_unlock(&device->fence_lock);
- if (signalled)
- return VK_SUCCESS;
- else
- return VK_NOT_READY;
+ fence->signalled = true;
+ return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL lvp_CreateFramebuffer(
@@ -1852,36 +1850,47 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_WaitForFences(
uint64_t timeout)
{
LVP_FROM_HANDLE(lvp_device, device, _device);
-
- VkResult qret = queue_wait_idle(&device->queue, timeout);
- bool timeout_status = false;
- if (qret == VK_TIMEOUT)
- return VK_TIMEOUT;
-
- mtx_lock(&device->fence_lock);
- for (unsigned i = 0; i < fenceCount; i++) {
- struct lvp_fence *fence = lvp_fence_from_handle(pFences[i]);
-
- if (fence->signaled)
- continue;
- if (!fence->handle) {
- timeout_status |= true;
- continue;
+ struct lvp_fence *fence = NULL;
+
+ /* lavapipe is completely synchronous, so only one fence needs to be waited on */
+ if (waitAll) {
+ /* find highest timeline id */
+ for (unsigned i = 0; i < fenceCount; i++) {
+ struct lvp_fence *f = lvp_fence_from_handle(pFences[i]);
+
+ /* this is an unsubmitted fence: immediately bail out */
+ if (!f->timeline)
+ return VK_TIMEOUT;
+ if (!fence || f->timeline > fence->timeline)
+ fence = f;
}
- bool ret = device->pscreen->fence_finish(device->pscreen,
- NULL,
- fence->handle,
- timeout);
- if (ret && !waitAll) {
- timeout_status = false;
- break;
+ } else {
+ /* find lowest timeline id */
+ for (unsigned i = 0; i < fenceCount; i++) {
+ struct lvp_fence *f = lvp_fence_from_handle(pFences[i]);
+ if (f->timeline && (!fence || f->timeline < fence->timeline))
+ fence = f;
}
+ }
+ if (!fence)
+ return VK_TIMEOUT;
+ if (fence->signalled)
+ return VK_SUCCESS;
+
+ if (!util_queue_fence_is_signalled(&fence->fence)) {
+ int64_t abs_timeout = os_time_get_absolute_timeout(timeout);
+ if (!util_queue_fence_wait_timeout(&fence->fence, abs_timeout))
+ return VK_TIMEOUT;
- if (!ret)
- timeout_status |= true;
+ int64_t time_ns = os_time_get_nano();
+ timeout = abs_timeout > time_ns ? abs_timeout - time_ns : 0;
}
- mtx_unlock(&device->fence_lock);
- return timeout_status ? VK_TIMEOUT : VK_SUCCESS;
+
+ if (!fence->handle ||
+ !device->pscreen->fence_finish(device->pscreen, NULL, fence->handle, timeout))
+ return VK_TIMEOUT;
+ fence->signalled = true;
+ return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL lvp_CreateSemaphore(
diff --git a/src/gallium/frontends/lavapipe/lvp_private.h b/src/gallium/frontends/lavapipe/lvp_private.h
index a56ac3f2e1f..c00e50e8477 100644
--- a/src/gallium/frontends/lavapipe/lvp_private.h
+++ b/src/gallium/frontends/lavapipe/lvp_private.h
@@ -31,6 +31,8 @@
#include "util/macros.h"
#include "util/list.h"
+#include "util/simple_mtx.h"
+#include "util/u_queue.h"
#include "compiler/shader_enums.h"
#include "pipe/p_screen.h"
@@ -166,10 +168,10 @@ struct lvp_queue {
struct pipe_context *ctx;
struct cso_context *cso;
bool shutdown;
- thrd_t exec_thread;
- mtx_t m;
- cnd_t new_work;
- struct list_head workqueue;
+ uint64_t timeline;
+ struct util_queue queue;
+ simple_mtx_t last_lock;
+ struct pipe_fence_handle *last_fence;
volatile int count;
};
@@ -193,8 +195,6 @@ struct lvp_device {
struct lvp_instance * instance;
struct lvp_physical_device *physical_device;
struct pipe_screen *pscreen;
-
- mtx_t fence_lock;
};
void lvp_device_get_cache_uuid(void *uuid);
@@ -497,8 +497,10 @@ struct lvp_event {
struct lvp_fence {
struct vk_object_base base;
- bool signaled;
+ uint64_t timeline;
+ struct util_queue_fence fence;
struct pipe_fence_handle *handle;
+ bool signalled;
};
struct lvp_semaphore {
@@ -1209,6 +1211,8 @@ lvp_vk_format_to_pipe_format(VkFormat format)
return vk_format_to_pipe_format(format);
}
+void
+queue_thread_noop(void *data, void *gdata, int thread_index);
#ifdef __cplusplus
}
#endif
diff --git a/src/gallium/frontends/lavapipe/lvp_wsi.c b/src/gallium/frontends/lavapipe/lvp_wsi.c
index 96cb28bd0be..6b1ecaacab0 100644
--- a/src/gallium/frontends/lavapipe/lvp_wsi.c
+++ b/src/gallium/frontends/lavapipe/lvp_wsi.c
@@ -240,7 +240,7 @@ VKAPI_ATTR VkResult VKAPI_CALL lvp_AcquireNextImage2KHR(
LVP_FROM_HANDLE(lvp_fence, fence, pAcquireInfo->fence);
if (fence && (result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)) {
- fence->signaled = true;
+ util_queue_add_job(&device->queue.queue, fence, &fence->fence, queue_thread_noop, NULL, 0);
}
return result;
}
More information about the mesa-commit
mailing list