freedreno: add synchronization between mesa and ddx
authorRob Clark <robclark@freedesktop.org>
Thu, 25 Apr 2013 20:36:15 +0000 (16:36 -0400)
committerRob Clark <robclark@freedesktop.org>
Thu, 25 Apr 2013 21:33:59 +0000 (17:33 -0400)
Super-cheezy way to synchronization between mesa and ddx..  the
SET_ACTIVE ioctl gives us a way to stash a 32b # w/ a GEM bo, and
GET_BUFINFO gives us a way to retrieve it.  We use this to stash
the timestamp of the last ISSUEIBCMDS on the buffer.

To avoid an obscene amount of syscalls, we:
 1) Only set the timestamp for buffers w/ an flink name, ie.
    only buffers shared across processes.  This is enough to
    catch the DRI2 buffers.
 2) Only set the timestamp for buffers submitted to the 3d ring
    and only check the timestamps on buffers submitted to the
    2d ring.  This should be enough to handle synchronizing of
    presentation blit.  We could do synchronization in the other
    direction too, but that would be problematic if we are using
    the 3d ring from DDX, since client side wouldn't know this.

The waiting on timestamp happens before flush, and setting of
timestamp happens after flush.  It is transparent to the user
of libdrm_freedreno as all the tracking of buffers happens via
_emit_reloc()..

Signed-off-by: Rob Clark <robclark@freedesktop.org>
freedreno/freedreno_bo.c
freedreno/freedreno_pipe.c
freedreno/freedreno_priv.h
freedreno/freedreno_ringbuffer.c

index 4f566e1..be386b4 100644 (file)
@@ -165,12 +165,17 @@ struct fd_bo * fd_bo_from_name(struct fd_device *dev, uint32_t name)
        struct drm_gem_open req = {
                        .name = name,
        };
+       struct fd_bo *bo;
 
        if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
                return NULL;
        }
 
-       return bo_from_handle(dev, req.size, req.handle);
+       bo = bo_from_handle(dev, req.size, req.handle);
+       if (bo)
+               bo->name = name;
+
+       return bo;
 }
 
 struct fd_bo * fd_bo_ref(struct fd_bo *bo)
@@ -272,3 +277,64 @@ uint32_t fd_bo_gpuaddr(struct fd_bo *bo, uint32_t offset)
        }
        return bo->gpuaddr + offset;
 }
+
+/*
+ * Super-cheezy way to synchronization between mesa and ddx..  the
+ * SET_ACTIVE ioctl gives us a way to stash a 32b # w/ a GEM bo, and
+ * GET_BUFINFO gives us a way to retrieve it.  We use this to stash
+ * the timestamp of the last ISSUEIBCMDS on the buffer.
+ *
+ * To avoid an obscene amount of syscalls, we:
+ *  1) Only set the timestamp for buffers w/ an flink name, ie.
+ *     only buffers shared across processes.  This is enough to
+ *     catch the DRI2 buffers.
+ *  2) Only set the timestamp for buffers submitted to the 3d ring
+ *     and only check the timestamps on buffers submitted to the
+ *     2d ring.  This should be enough to handle synchronizing of
+ *     presentation blit.  We could do synchronization in the other
+ *     direction too, but that would be problematic if we are using
+ *     the 3d ring from DDX, since client side wouldn't know this.
+ *
+ * The waiting on timestamp happens before flush, and setting of
+ * timestamp happens after flush.  It is transparent to the user
+ * of libdrm_freedreno as all the tracking of buffers happens via
+ * _emit_reloc()..
+ */
+
+void fb_bo_set_timestamp(struct fd_bo *bo, uint32_t timestamp)
+{
+       if (bo->name) {
+               struct drm_kgsl_gem_active req = {
+                               .handle = bo->handle,
+                               .active = timestamp,
+               };
+               int ret;
+
+               ret = drmCommandWrite(bo->dev->fd, DRM_KGSL_GEM_SET_ACTIVE,
+                               &req, sizeof(req));
+               if (ret) {
+                       ERROR_MSG("set active failed: %s", strerror(errno));
+               }
+       }
+}
+
+uint32_t fd_bo_get_timestamp(struct fd_bo *bo)
+{
+       uint32_t timestamp = 0;
+       if (bo->name) {
+               struct drm_kgsl_gem_bufinfo req = {
+                               .handle = bo->handle,
+               };
+               int ret;
+
+               ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
+                               &req, sizeof(req));
+               if (ret) {
+                       ERROR_MSG("get bufinfo failed: %s", strerror(errno));
+                       return 0;
+               }
+
+               timestamp = req.active;
+       }
+       return timestamp;
+}
index 1698c46..9f9f1c6 100644 (file)
@@ -199,8 +199,26 @@ void fd_pipe_add_submit(struct fd_pipe *pipe, struct fd_bo *bo)
        list_addtail(list, &pipe->submit_list);
 }
 
+/* prepare buffers on submit list before flush: */
+void fd_pipe_pre_submit(struct fd_pipe *pipe)
+{
+       struct fd_bo *bo;
+
+       if (pipe->id == FD_PIPE_3D)
+               return;
+
+       if (!pipe->p3d)
+               pipe->p3d = fd_pipe_new(pipe->dev, FD_PIPE_3D);
+
+       LIST_FOR_EACH_ENTRY(bo, &pipe->submit_list, list[pipe->id]) {
+               uint32_t timestamp = fd_bo_get_timestamp(bo);
+               if (timestamp)
+                       fd_pipe_wait(pipe->p3d, timestamp);
+       }
+}
+
 /* process buffers on submit list after flush: */
-void fd_pipe_process_submit(struct fd_pipe *pipe, uint32_t timestamp)
+void fd_pipe_post_submit(struct fd_pipe *pipe, uint32_t timestamp)
 {
        struct fd_bo *bo, *tmp;
 
@@ -209,6 +227,9 @@ void fd_pipe_process_submit(struct fd_pipe *pipe, uint32_t timestamp)
                list_del(list);
                bo->timestamp[pipe->id] = timestamp;
                list_addtail(list, &pipe->pending_list);
+
+               if (pipe->id == FD_PIPE_3D)
+                       fb_bo_set_timestamp(bo, timestamp);
        }
 
        if (!fd_pipe_timestamp(pipe, &timestamp))
index aa71b56..0edca1d 100644 (file)
@@ -70,10 +70,16 @@ struct fd_pipe {
         * not passed yet (so still ref'd in active cmdstream)
         */
        struct list_head pending_list;
+
+       /* if we are the 2d pipe, and want to wait on a timestamp
+        * from 3d, we need to also internally open the 3d pipe:
+        */
+       struct fd_pipe *p3d;
 };
 
 void fd_pipe_add_submit(struct fd_pipe *pipe, struct fd_bo *bo);
-void fd_pipe_process_submit(struct fd_pipe *pipe, uint32_t timestamp);
+void fd_pipe_pre_submit(struct fd_pipe *pipe);
+void fd_pipe_post_submit(struct fd_pipe *pipe, uint32_t timestamp);
 void fd_pipe_process_pending(struct fd_pipe *pipe, uint32_t timestamp);
 
 struct fd_bo {
@@ -95,6 +101,8 @@ struct fd_bo {
  * a proper kernel driver
  */
 uint32_t fd_bo_gpuaddr(struct fd_bo *bo, uint32_t offset);
+void fb_bo_set_timestamp(struct fd_bo *bo, uint32_t timestamp);
+uint32_t fd_bo_get_timestamp(struct fd_bo *bo);
 
 #define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1))
 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
index f187996..d20a7f5 100644 (file)
@@ -159,6 +159,8 @@ static int flush_impl(struct fd_ringbuffer *ring, uint32_t *last_start)
        };
        int ret;
 
+       fd_pipe_pre_submit(ring->pipe);
+
        /* z180_cmdstream_issueibcmds() is made of fail: */
        if (ring->pipe->id == FD_PIPE_2D) {
                /* fix up size field in last cmd packet */
@@ -180,7 +182,7 @@ static int flush_impl(struct fd_ringbuffer *ring, uint32_t *last_start)
        ring->last_timestamp = req.timestamp;
        ring->last_start = ring->cur;
 
-       fd_pipe_process_submit(ring->pipe, req.timestamp);
+       fd_pipe_post_submit(ring->pipe, req.timestamp);
 
        return ret;
 }