nouveau: add callbacks for signalled fences
authorBen Skeggs <skeggsb@gmail.com>
Mon, 10 Dec 2007 02:19:47 +0000 (13:19 +1100)
committerBen Skeggs <skeggsb@gmail.com>
Mon, 10 Dec 2007 02:19:47 +0000 (13:19 +1100)
src/mesa/drivers/dri/nouveau_winsys/nouveau_drmif.h
src/mesa/drivers/dri/nouveau_winsys/nouveau_fence.c
src/mesa/drivers/dri/nouveau_winsys/nouveau_pushbuf.c

index 1c2a2c12cff239917fb810919d75e8285aee7990..6d6633fac309b338f47e17d1f12d70ca2fb17fb9 100644 (file)
@@ -64,11 +64,18 @@ struct nouveau_fence {
        struct nouveau_channel *channel;
 };
 
+struct nouveau_fence_cb {
+       struct nouveau_fence_cb *next;
+       void (*func)(void *);
+       void *priv;
+};
+
 struct nouveau_fence_priv {
        struct nouveau_fence base;
        int refcount;
 
        struct nouveau_fence *next;
+       struct nouveau_fence_cb *signal_cb;
 
        uint32_t sequence;
        int emitted;
@@ -85,6 +92,8 @@ nouveau_fence_ref(struct nouveau_fence *, struct nouveau_fence **);
 extern void
 nouveau_fence_del(struct nouveau_fence **);
 
+extern int
+nouveau_fence_signal_cb(struct nouveau_fence *, void (*)(void *), void *);
 extern void
 nouveau_fence_emit(struct nouveau_fence *);
 
index 842b38f913b32b6f877ba75fe90fab02f50e1342..7861b6f84d386a9a0247831a51cce4c13ebafd79 100644 (file)
@@ -76,6 +76,27 @@ nouveau_fence_del(struct nouveau_fence **fence)
        }
 }
 
+int
+nouveau_fence_signal_cb(struct nouveau_fence *fence, void (*func)(void *),
+                       void *priv)
+{
+       struct nouveau_fence_priv *nvfence = nouveau_fence(fence);
+       struct nouveau_fence_cb *cb;
+
+       if (!nvfence || !func)
+               return -EINVAL;
+
+       cb = malloc(sizeof(struct nouveau_fence_cb));
+       if (!cb)
+               return -ENOMEM;
+
+       cb->func = func;
+       cb->priv = priv;
+       cb->next = nvfence->signal_cb;
+       nvfence->signal_cb = cb;
+       return 0;
+}
+
 void
 nouveau_fence_emit(struct nouveau_fence *fence)
 {
@@ -102,16 +123,33 @@ void
 nouveau_fence_flush(struct nouveau_channel *chan)
 {
        struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
-       struct nouveau_fence_priv *nvfence = nouveau_fence(nvchan->fence_head);
        uint32_t sequence = *nvchan->ref_cnt;
 
-       while (nvchan->fence_head && nvfence->sequence <= sequence) {
-               nvfence->signalled = 1;
+       while (nvchan->fence_head) {
+               struct nouveau_fence *fence = NULL;
+               struct nouveau_fence_priv *nvfence;
+       
+               nouveau_fence_ref(nvchan->fence_head, &fence);
+               nvfence = nouveau_fence(nvchan->fence_head);
+
+               if (nvfence->sequence > sequence) {
+                       nouveau_fence_del(&fence);
+                       break;
+               }
 
                nvchan->fence_head = nvfence->next;
                if (nvchan->fence_head == NULL)
                        nvchan->fence_tail = NULL;
-               nvfence = nouveau_fence(nvchan->fence_head);
+               nvfence->signalled = 1;
+
+               while (nvfence->signal_cb) {
+                       struct nouveau_fence_cb *cb = nvfence->signal_cb;
+                       nvfence->signal_cb = cb->next;
+                       cb->func(cb->priv);
+                       free(cb);
+               }
+
+               nouveau_fence_del(&fence);
        }
 }
 
index 551889f94c7f11babc879261916b7d55fce5ac8d..a922300ff5e441235db3c497873096851f5a4446 100644 (file)
@@ -48,6 +48,16 @@ nouveau_pushbuf_init(struct nouveau_channel *chan)
        return 0;
 }
 
+static void
+nouveau_pushbuf_fence_signalled(void *priv)
+{
+       struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(priv);
+
+       nouveau_fence_del(&nvpb->fence);
+       nouveau_resource_free(&nvpb->res);
+       free(nvpb);
+}
+
 /* This would be our TTM "superioctl" */
 int
 nouveau_pushbuf_flush(struct nouveau_channel *chan)
@@ -64,6 +74,7 @@ nouveau_pushbuf_flush(struct nouveau_channel *chan)
 
        if (nvpb->base.remaining == nvpb->res->size / 4)
                return 0;
+       nvchan->pb_tail = NULL;
 
        ret = nouveau_fence_new(chan, &fence);
        if (ret)
@@ -124,76 +135,35 @@ nouveau_pushbuf_flush(struct nouveau_channel *chan)
 
        /* Fence */
        nvpb->fence = fence;
+       nouveau_fence_signal_cb(nvpb->fence, nouveau_pushbuf_fence_signalled,
+                               nvpb);
        nouveau_fence_emit(nvpb->fence);
 
        /* Kickoff */
        FIRE_RING_CH(chan);
 
+       if (sync_hack) {
+               struct nouveau_fence *f = NULL;
+               nouveau_fence_ref(nvpb->fence, &f);
+               nouveau_fence_wait(&f);
+       }
+
        /* Allocate space for next push buffer */
 out_realloc:
        nvpb = calloc(1, sizeof(struct nouveau_pushbuf_priv));
        if (!nvpb)
                return -ENOMEM;
 
-       if (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, NULL, &nvpb->res)) {
-               struct nouveau_pushbuf_priv *e;
-               int nr = 0;
-
-               /* Update fences */
+       while (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, NULL,
+                                     &nvpb->res)) {
                nouveau_fence_flush(chan);
-
-               /* Free any push buffers that have already been executed */
-               e = nouveau_pushbuf(nvchan->pb_head);
-               while (e && e->fence) {
-                       if (!e->fence || !nouveau_fence(e->fence)->signalled)
-                               break;
-                       nouveau_fence_del(&e->fence);
-                       nouveau_resource_free(&e->res);
-                       nr++;
-
-                       nvchan->pb_head = e->next;
-                       if (nvchan->pb_head == NULL)
-                               nvchan->pb_tail = NULL;
-                       free(e);
-                       e = nouveau_pushbuf(nvchan->pb_head);
-               }
-
-               /* We didn't free any buffers above.  As a last resort, busy
-                * wait on the oldest buffer becoming available.
-                */
-               if (!nr) {
-                       e = nouveau_pushbuf(nvchan->pb_head);
-                       nouveau_fence_wait(&e->fence);
-                       nouveau_resource_free(&e->res);
-
-                       nvchan->pb_head = e->next;
-                       if (nvchan->pb_head == NULL)
-                               nvchan->pb_tail = NULL;
-                       free(e);
-               }
-
-               if (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, nvpb,
-                                          &nvpb->res))
-                       assert(0);
        }
 
        nvpb->base.channel = chan;
        nvpb->base.remaining = nvpb->res->size / 4;
        nvpb->base.cur = &nvchan->pushbuf[nvpb->res->start/4];
-
-       if (nvchan->pb_tail) {
-               nouveau_pushbuf(nvchan->pb_tail)->next = &nvpb->base;
-       } else {
-               nvchan->pb_head = &nvpb->base;
-       }
        nvchan->pb_tail = &nvpb->base;
 
-       if (sync_hack) {
-               struct nouveau_fence *f = NULL;
-               nouveau_fence_ref(nvpb->fence, &f);
-               nouveau_fence_wait(&f);
-       }
-
        return 0;
 }