From 0a14b53f4dcb38e852db14d186d14c8526f3549a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20D=C3=B6singer?= <stefan@codeweavers.com>
Date: Sat, 6 Jul 2013 19:18:03 +0200
Subject: wined3d: Introduce a separate queue for priority commands

---
 dlls/wined3d/cs.c              | 128 ++++++++++++++++++++++++++++++++++-------
 dlls/wined3d/wined3d_private.h |   5 +-
 2 files changed, 110 insertions(+), 23 deletions(-)

diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
index 68e1ed7..a7f7694 100644
--- a/dlls/wined3d/cs.c
+++ b/dlls/wined3d/cs.c
@@ -372,7 +372,7 @@ struct wined3d_cs_query_issue
     DWORD flags;
 };
 
-static void wined3d_cs_submit(struct wined3d_cs *cs, size_t size)
+static void wined3d_cs_mt_submit(struct wined3d_cs *cs, size_t size)
 {
     LONG new_val = (cs->queue.head + size) & (WINED3D_CS_QUEUE_SIZE - 1);
     /* There is only one thread writing to queue.head, InterlockedExchange
@@ -380,6 +380,14 @@ static void wined3d_cs_submit(struct wined3d_cs *cs, size_t size)
     InterlockedExchange(&cs->queue.head, new_val);
 }
 
+static void wined3d_cs_mt_submit_prio(struct wined3d_cs *cs, size_t size)
+{
+    LONG new_val = (cs->prio_queue.head + size) & (WINED3D_CS_QUEUE_SIZE - 1);
+    /* There is only one thread writing to queue.head, InterlockedExchange
+     * is used for the memory barrier. */
+    InterlockedExchange(&cs->prio_queue.head, new_val);
+}
+
 static UINT wined3d_cs_exec_nop(struct wined3d_cs *cs, const void *data)
 {
     return sizeof(enum wined3d_cs_op);
@@ -413,15 +421,16 @@ static void wined3d_cs_emit_fence(struct wined3d_cs *cs, BOOL *signalled)
     cs->ops->submit(cs, sizeof(*op));
 }
 
-static void wined3d_cs_finish(struct wined3d_cs *cs)
+static void wined3d_cs_emit_fence_prio(struct wined3d_cs *cs, BOOL *signalled)
 {
-    BOOL fence;
+    struct wined3d_cs_fence *op;
 
-    wined3d_cs_emit_fence(cs, &fence);
+    *signalled = FALSE;
 
-    /* A busy wait should be fine, we're not supposed to have to wait very
-     * long. */
-    while (!InterlockedCompareExchange(&fence, TRUE, TRUE));
+    op = cs->ops->require_space_prio(cs, sizeof(*op));
+    op->opcode = WINED3D_CS_OP_FENCE;
+    op->signalled = signalled;
+    cs->ops->submit_prio(cs, sizeof(*op));
 }
 
 static UINT wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data)
@@ -1765,9 +1774,9 @@ static UINT (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void
     /* WINED3D_CS_OP_QUERY_ISSUE                */ wined3d_cs_exec_query_issue,
 };
 
-static void *wined3d_cs_mt_require_space(struct wined3d_cs *cs, size_t size)
+static inline void *_wined3d_cs_mt_require_space(struct wined3d_cs *cs, size_t size, BOOL prio)
 {
-    struct wined3d_cs_queue *queue = &cs->queue;
+    struct wined3d_cs_queue *queue = prio ? &cs->prio_queue : &cs->queue;
     size_t queue_size = sizeof(queue->data) / sizeof(*queue->data);
 
     if (queue_size - size < queue->head)
@@ -1775,7 +1784,7 @@ static void *wined3d_cs_mt_require_space(struct wined3d_cs *cs, size_t size)
         struct wined3d_cs_skip *skip;
         size_t nop_size = queue_size - queue->head;
 
-        skip = wined3d_cs_mt_require_space(cs, nop_size);
+        skip = _wined3d_cs_mt_require_space(cs, nop_size, prio);
         if (nop_size < sizeof(*skip))
         {
             skip->opcode = WINED3D_CS_OP_NOP;
@@ -1786,7 +1795,11 @@ static void *wined3d_cs_mt_require_space(struct wined3d_cs *cs, size_t size)
             skip->size = nop_size;
         }
 
-        cs->ops->submit(cs, nop_size);
+        if (prio)
+            cs->ops->submit_prio(cs, nop_size);
+        else
+            cs->ops->submit(cs, nop_size);
+
         assert(!queue->head);
     }
 
@@ -1813,12 +1826,15 @@ static void *wined3d_cs_mt_require_space(struct wined3d_cs *cs, size_t size)
     return &queue->data[queue->head];
 }
 
-static const struct wined3d_cs_ops wined3d_cs_mt_ops =
+static inline void *wined3d_cs_mt_require_space(struct wined3d_cs *cs, size_t size)
 {
-    wined3d_cs_mt_require_space,
-    wined3d_cs_submit,
-    wined3d_cs_finish,
-};
+    return _wined3d_cs_mt_require_space(cs, size, FALSE);
+}
+
+static inline void *wined3d_cs_mt_require_space_prio(struct wined3d_cs *cs, size_t size)
+{
+    return _wined3d_cs_mt_require_space(cs, size, TRUE);
+}
 
 /* FIXME: wined3d_device_uninit_3d() should either flush and wait, or be an
  * OP itself. */
@@ -1829,9 +1845,63 @@ static void wined3d_cs_emit_stop(struct wined3d_cs *cs)
     op = wined3d_cs_mt_require_space(cs, sizeof(*op));
     op->opcode = WINED3D_CS_OP_STOP;
 
-    wined3d_cs_submit(cs, sizeof(*op));
+    wined3d_cs_mt_submit(cs, sizeof(*op));
 }
 
+static void wined3d_cs_mt_finish(struct wined3d_cs *cs)
+{
+    BOOL fence;
+
+    if (cs->thread_id == GetCurrentThreadId())
+    {
+        static BOOL once;
+        if (!once)
+        {
+            FIXME("flush_and_wait called from cs thread\n");
+            once = TRUE;
+        }
+        return;
+    }
+
+    wined3d_cs_emit_fence(cs, &fence);
+
+    /* A busy wait should be fine, we're not supposed to have to wait very
+     * long. */
+    while (!InterlockedCompareExchange(&fence, TRUE, TRUE));
+}
+
+static void wined3d_cs_mt_finish_prio(struct wined3d_cs *cs)
+{
+    BOOL fence;
+
+    if (cs->thread_id == GetCurrentThreadId())
+    {
+        static BOOL once;
+        if (!once)
+        {
+            FIXME("flush_and_wait called from cs thread\n");
+            once = TRUE;
+        }
+        return;
+    }
+
+    wined3d_cs_emit_fence_prio(cs, &fence);
+
+    /* A busy wait should be fine, we're not supposed to have to wait very
+     * long. */
+    while (!InterlockedCompareExchange(&fence, TRUE, TRUE));
+}
+
+static const struct wined3d_cs_ops wined3d_cs_mt_ops =
+{
+    wined3d_cs_mt_require_space,
+    wined3d_cs_mt_require_space_prio,
+    wined3d_cs_mt_submit,
+    wined3d_cs_mt_submit_prio,
+    wined3d_cs_mt_finish,
+    wined3d_cs_mt_finish_prio,
+};
+
 static void wined3d_cs_st_submit(struct wined3d_cs *cs, size_t size)
 {
     enum wined3d_cs_op opcode = *(const enum wined3d_cs_op *)&cs->queue.data;
@@ -1857,8 +1927,11 @@ static void *wined3d_cs_st_require_space(struct wined3d_cs *cs, size_t size)
 static const struct wined3d_cs_ops wined3d_cs_st_ops =
 {
     wined3d_cs_st_require_space,
+    wined3d_cs_st_require_space,
+    wined3d_cs_st_submit,
     wined3d_cs_st_submit,
     wined3d_cs_st_finish,
+    wined3d_cs_st_finish,
 };
 
 void wined3d_cs_switch_onscreen_ds(struct wined3d_cs *cs,
@@ -1901,6 +1974,7 @@ static DWORD WINAPI wined3d_cs_run(void *thread_param)
     enum wined3d_cs_op opcode;
     LONG tail;
     char poll = 0;
+    struct wined3d_cs_queue *queue;
 
     TRACE("Started.\n");
 
@@ -1916,13 +1990,23 @@ static DWORD WINAPI wined3d_cs_run(void *thread_param)
         else
             poll++;
 
-        if (*((volatile LONG *)&cs->queue.head) == cs->queue.tail)
+        if (*((volatile LONG *)&cs->prio_queue.head) != cs->prio_queue.tail)
+        {
+            queue = &cs->prio_queue;
+        }
+        else if (*((volatile LONG *)&cs->queue.head) != cs->queue.tail)
+        {
+            queue = &cs->queue;
+            if (*((volatile LONG *)&cs->prio_queue.head) != cs->prio_queue.tail)
+                queue = &cs->prio_queue;
+        }
+        else
         {
             continue;
         }
 
-        tail = cs->queue.tail;
-        opcode = *(const enum wined3d_cs_op *)&cs->queue.data[tail];
+        tail = queue->tail;
+        opcode = *(const enum wined3d_cs_op *)&queue->data[tail];
 
         if (opcode >= WINED3D_CS_OP_STOP)
         {
@@ -1931,9 +2015,9 @@ static DWORD WINAPI wined3d_cs_run(void *thread_param)
             goto done;
         }
 
-        tail += wined3d_cs_op_handlers[opcode](cs, &cs->queue.data[tail]);
+        tail += wined3d_cs_op_handlers[opcode](cs, &queue->data[tail]);
         tail &= (WINED3D_CS_QUEUE_SIZE - 1);
-        InterlockedExchange(&cs->queue.tail, tail);
+        InterlockedExchange(&queue->tail, tail);
     }
 
 done:
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 7bda9f0..cf01a6a 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -2550,8 +2550,11 @@ struct wined3d_cs_queue
 struct wined3d_cs_ops
 {
     void *(*require_space)(struct wined3d_cs *cs, size_t size);
+    void *(*require_space_prio)(struct wined3d_cs *cs, size_t size);
     void (*submit)(struct wined3d_cs *cs, size_t size);
+    void (*submit_prio)(struct wined3d_cs *cs, size_t size);
     void (*finish)(struct wined3d_cs *cs);
+    void (*finish_prio)(struct wined3d_cs *cs);
 };
 
 struct wined3d_cs
@@ -2563,7 +2566,7 @@ struct wined3d_cs
     DWORD thread_id;
     struct wined3d_surface *onscreen_depth_stencil;
 
-    struct wined3d_cs_queue queue;
+    struct wined3d_cs_queue queue, prio_queue;
 
     LONG pending_presents;
     struct list query_poll_list;
-- 
2.1.3

