nouveau: track last validated offsets, so we know when relocs can be avoided.
[mesa.git] / src / mesa / drivers / dri / nouveau_winsys / nouveau_bo.c
index f1981b977706460dad59c98e564c144fa40725c5..288674f231b6d517e32d6e683f5f35a04e409e58 100644 (file)
 #include <stdint.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <assert.h>
 
 #include "nouveau_drmif.h"
 #include "nouveau_dma.h"
 #include "nouveau_local.h"
 
-int
-nouveau_bo_init(struct nouveau_device *userdev)
+static void
+nouveau_mem_free(struct nouveau_device *dev, struct drm_nouveau_mem_alloc *ma,
+                void **map)
 {
-       return 0;
-}
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
+       struct drm_nouveau_mem_free mf;
 
-void
-nouveau_bo_takedown(struct nouveau_device *userdev)
-{
+       if (map && *map) {
+               drmUnmap(*map, ma->size);
+               *map = NULL;
+       }
+
+       if (ma->size) {
+               mf.offset = ma->offset;
+               mf.flags = ma->flags;
+               drmCommandWrite(nvdev->fd, DRM_NOUVEAU_MEM_FREE,
+                               &mf, sizeof(mf));
+               ma->size = 0;
+       }
 }
 
 static int
-nouveau_bo_realloc_gpu(struct nouveau_bo_priv *bo, uint32_t flags, int size)
+nouveau_mem_alloc(struct nouveau_device *dev, unsigned size, unsigned align,
+                 uint32_t flags, struct drm_nouveau_mem_alloc *ma, void **map)
 {
-       struct nouveau_device_priv *nv = nouveau_device(bo->base.device);
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
        int ret;
 
-       if (bo->drm.size && bo->drm.size != size) {
-               struct drm_nouveau_mem_free f;
-
-               if (bo->map) {
-                       drmUnmap(bo->map, bo->drm.size);
-                       bo->map = NULL;
+       ma->alignment = align;
+       ma->size = size;
+       ma->flags = flags;
+       if (map)
+               ma->flags |= NOUVEAU_MEM_MAPPED;
+       ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_MEM_ALLOC, ma,
+                                 sizeof(struct drm_nouveau_mem_alloc));
+       if (ret)
+               return ret;
+
+       if (map) {
+               ret = drmMap(nvdev->fd, ma->map_handle, ma->size, map);
+               if (ret) {
+                       *map = NULL;
+                       nouveau_mem_free(dev, ma, map);
+                       return ret;
                }
+       }
 
-               f.flags = bo->drm.flags;
-               f.offset = bo->drm.offset;
-               drmCommandWrite(nv->fd, DRM_NOUVEAU_MEM_FREE, &f, sizeof(f));
+       return 0;
+}
+
+static int
+nouveau_bo_realloc_gpu(struct nouveau_bo_priv *nvbo, uint32_t flags, int size)
+{
+       int ret;
 
-               bo->drm.size = 0;
+       if (nvbo->drm.size && nvbo->drm.size != size) {
+               nouveau_mem_free(nvbo->base.device, &nvbo->drm, &nvbo->map);
        }
 
-       if (size && !bo->drm.size) {
+       if (size && !nvbo->drm.size) {
                if (flags) {
-                       bo->drm.flags = 0;
+                       nvbo->drm.flags = 0;
                        if (flags & NOUVEAU_BO_VRAM)
-                               bo->drm.flags |= NOUVEAU_MEM_FB;
+                               nvbo->drm.flags |= NOUVEAU_MEM_FB;
                        if (flags & NOUVEAU_BO_GART)
-                               bo->drm.flags |= (NOUVEAU_MEM_AGP |
-                                                 NOUVEAU_MEM_PCI);
-                       bo->drm.flags |= NOUVEAU_MEM_MAPPED;
+                               nvbo->drm.flags |= (NOUVEAU_MEM_AGP |
+                                                   NOUVEAU_MEM_PCI);
+                       nvbo->drm.flags |= NOUVEAU_MEM_MAPPED;
                }
 
-               bo->drm.size = size;
-               
-               ret = drmCommandWriteRead(nv->fd, DRM_NOUVEAU_MEM_ALLOC,
-                                         &bo->drm, sizeof(bo->drm));
+               ret = nouveau_mem_alloc(nvbo->base.device, size,
+                                       nvbo->drm.alignment, nvbo->drm.flags,
+                                       &nvbo->drm, &nvbo->map);
                if (ret) {
-                       free(bo);
-                       return ret;
+                       assert(0);
                }
+       }
 
-               ret = drmMap(nv->fd, bo->drm.map_handle, bo->drm.size,
-                            &bo->map);
-               if (ret) {
-                       bo->map = NULL;
-                       nouveau_bo_del((void *)&bo);
-                       return ret;
-               }
+       return 0;
+}
+
+static void
+nouveau_bo_tmp_del(void *priv)
+{
+       struct nouveau_resource *r = priv;
+
+       nouveau_fence_ref(NULL, (struct nouveau_fence **)&r->priv);
+       nouveau_resource_free(&r);
+}
+
+static struct nouveau_resource *
+nouveau_bo_tmp(struct nouveau_channel *chan, unsigned size,
+              struct nouveau_fence *fence)
+{
+       struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
+       struct nouveau_resource *r = NULL;
+       struct nouveau_fence *ref = NULL;
+
+       if (fence)
+               nouveau_fence_ref(fence, &ref);
+       else
+               nouveau_fence_new(chan, &ref);
+       assert(ref);
+
+       while (nouveau_resource_alloc(nvdev->sa_heap, size, ref, &r)) {
+               nouveau_fence_flush(chan);
+       }
+       nouveau_fence_signal_cb(ref, nouveau_bo_tmp_del, r);
+
+       return r;
+}
+
+int
+nouveau_bo_init(struct nouveau_device *dev)
+{
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
+       int ret;
+
+       ret = nouveau_mem_alloc(dev, 128*1024, 0, NOUVEAU_MEM_AGP |
+                               NOUVEAU_MEM_PCI, &nvdev->sa, &nvdev->sa_map);
+       if (ret)
+               return ret;
+
+       ret = nouveau_resource_init(&nvdev->sa_heap, 0, nvdev->sa.size);
+       if (ret) {
+               nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
+               return ret;
        }
 
        return 0;
 }
 
+void
+nouveau_bo_takedown(struct nouveau_device *dev)
+{
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
+
+       nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
+}
+
 int
-nouveau_bo_new(struct nouveau_device *userdev, uint32_t flags, int align,
-              int size, struct nouveau_bo **userbo)
+nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, int align,
+              int size, struct nouveau_bo **bo)
 {
-       struct nouveau_bo_priv *bo;
+       struct nouveau_bo_priv *nvbo;
        int ret;
 
-       if (!userdev || !userbo || *userbo)
+       if (!dev || !bo || *bo)
                return -EINVAL;
 
-       bo = calloc(1, sizeof(*bo));
-       if (!bo)
+       nvbo = calloc(1, sizeof(struct nouveau_bo_priv));
+       if (!nvbo)
                return -ENOMEM;
-       bo->base.device = userdev;
-       bo->drm.alignment = align;
+       nvbo->base.device = dev;
+       nvbo->drm.alignment = align;
 
        if (flags & NOUVEAU_BO_PIN) {
-               ret = nouveau_bo_realloc_gpu(bo, flags, size);
+               ret = nouveau_bo_realloc_gpu(nvbo, flags, size);
                if (ret) {
-                       free(bo);
+                       free(nvbo);
                        return ret;
                }       
        } else {
-               bo->sysmem = malloc(size);
-               if (!bo->sysmem) {
-                       free(bo);
+               nvbo->sysmem = malloc(size);
+               if (!nvbo->sysmem) {
+                       free(nvbo);
                        return -ENOMEM;
                }
        }
 
-       bo->base.size = size;
-       bo->base.offset = bo->drm.offset;
-       bo->base.handle = (unsigned long)bo;
-       bo->refcount = 1;
-       *userbo = &bo->base;
+       nvbo->base.size = size;
+       nvbo->base.offset = nvbo->drm.offset;
+       nvbo->base.handle = bo_to_ptr(nvbo);
+       nvbo->refcount = 1;
+       *bo = &nvbo->base;
        return 0;
 }
 
 int
-nouveau_bo_user(struct nouveau_device *userdev, void *ptr, int size,
-               struct nouveau_bo **userbo)
+nouveau_bo_user(struct nouveau_device *dev, void *ptr, int size,
+               struct nouveau_bo **bo)
 {
-       struct nouveau_bo_priv *bo;
+       struct nouveau_bo_priv *nvbo;
 
-       if (!userdev || !userbo || *userbo)
+       if (!dev || !bo || *bo)
                return -EINVAL;
 
-       bo = calloc(1, sizeof(*bo));
-       if (!bo)
+       nvbo = calloc(1, sizeof(*nvbo));
+       if (!nvbo)
                return -ENOMEM;
-       bo->base.device = userdev;
+       nvbo->base.device = dev;
        
-       bo->sysmem = ptr;
-       bo->user = 1;
-
-       bo->base.size = size;
-       bo->base.offset = bo->drm.offset;
-       bo->base.handle = (unsigned long)bo;
-       bo->refcount = 1;
-       *userbo = &bo->base;
+       nvbo->sysmem = ptr;
+       nvbo->user = 1;
+
+       nvbo->base.size = size;
+       nvbo->base.offset = nvbo->drm.offset;
+       nvbo->base.handle = bo_to_ptr(nvbo);
+       nvbo->refcount = 1;
+       *bo = &nvbo->base;
        return 0;
 }
 
 int
-nouveau_bo_ref(struct nouveau_device *userdev, uint64_t handle,
-              struct nouveau_bo **userbo)
+nouveau_bo_ref(struct nouveau_device *dev, uint64_t handle,
+              struct nouveau_bo **bo)
 {
-       struct nouveau_bo_priv *bo = (void *)(unsigned long)handle;
+       struct nouveau_bo_priv *nvbo = ptr_to_bo(handle);
 
-       if (!userdev || !userbo || *userbo)
+       if (!dev || !bo || *bo)
                return -EINVAL;
 
-       bo->refcount++;
-       *userbo = &bo->base;
+       nvbo->refcount++;
+       *bo = &nvbo->base;
        return 0;
 }
 
 int
-nouveau_bo_resize(struct nouveau_bo *userbo, int size)
+nouveau_bo_resize(struct nouveau_bo *bo, int size)
 {
-       struct nouveau_bo_priv *bo = nouveau_bo(userbo);
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
        int ret;
 
-       if (!bo || bo->user)
+       if (!nvbo || nvbo->user)
                return -EINVAL;
 
-       if (bo->sysmem) {
-               bo->sysmem = realloc(bo->sysmem, size);
-               if (!bo->sysmem)
+       if (nvbo->sysmem) {
+               nvbo->sysmem = realloc(nvbo->sysmem, size);
+               if (!nvbo->sysmem)
                        return -ENOMEM;
        } else {
-               ret = nouveau_bo_realloc_gpu(bo, 0, size);
+               ret = nouveau_bo_realloc_gpu(nvbo, 0, size);
                if (ret)
                        return ret;
        }
 
-       bo->base.size = size;
+       nvbo->base.size = size;
        return 0;
 }
 
 void
-nouveau_bo_del(struct nouveau_bo **userbo)
+nouveau_bo_del(struct nouveau_bo **bo)
 {
-       struct nouveau_bo_priv *bo;
+       struct nouveau_bo_priv *nvbo;
 
-       if (!userbo || !*userbo)
+       if (!bo || !*bo)
                return;
-       bo = nouveau_bo(*userbo);
-       *userbo = NULL;
+       nvbo = nouveau_bo(*bo);
+       *bo = NULL;
 
-       if (--bo->refcount)
+       if (--nvbo->refcount)
                return;
 
-       nouveau_bo_realloc_gpu(bo, 0, 0);
-       if (bo->sysmem && !bo->user)
-               free(bo->sysmem);
-       free(bo);
+       if (nvbo->fence)
+               nouveau_fence_wait(&nvbo->fence);
+
+       nouveau_bo_realloc_gpu(nvbo, 0, 0);
+       if (nvbo->sysmem && !nvbo->user)
+               free(nvbo->sysmem);
+       free(nvbo);
 }
 
 int
-nouveau_bo_map(struct nouveau_bo *userbo, uint32_t flags)
+nouveau_bo_map(struct nouveau_bo *bo, uint32_t flags)
 {
-       struct nouveau_bo_priv *bo = nouveau_bo(userbo);
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
 
-       if (!bo)
+       if (!nvbo)
                return -EINVAL;
 
-       if (bo->sysmem)
-               userbo->map = bo->sysmem;
+       if (flags & NOUVEAU_BO_WR)
+               nouveau_fence_wait(&nvbo->fence);
+       else
+               nouveau_fence_wait(&nvbo->wr_fence);
+
+       if (nvbo->sysmem)
+               bo->map = nvbo->sysmem;
        else
-               userbo->map = bo->map;
+               bo->map = nvbo->map;
        return 0;
 }
 
 void
-nouveau_bo_unmap(struct nouveau_bo *userbo)
+nouveau_bo_unmap(struct nouveau_bo *bo)
 {
-       userbo->map = NULL;
+       bo->map = NULL;
 }
 
-void
-nouveau_bo_emit_reloc(struct nouveau_channel *userchan, void *ptr,
-                     struct nouveau_bo *userbo, uint32_t data, uint32_t flags,
-                     uint32_t vor, uint32_t tor)
+static int
+nouveau_bo_upload(struct nouveau_bo_priv *nvbo)
 {
-       struct nouveau_channel_priv *chan = nouveau_channel(userchan);
-       struct nouveau_bo_priv *bo = nouveau_bo(userbo);
-       struct nouveau_bo_reloc *r;
-       int i, on_list = 0;
-
-       for (i = 0; i < chan->nr_buffers; i++) {
-               if (chan->buffers[i].bo == bo) {
-                       on_list = 1;
-                       break;
-               }
-       }
+       if (nvbo->fence)
+               nouveau_fence_wait(&nvbo->fence);
+       memcpy(nvbo->map, nvbo->sysmem, nvbo->drm.size);
+       return 0;
+}
 
-       if (i >= 128)
-               return;
+static int
+nouveau_bo_validate_user(struct nouveau_channel *chan, struct nouveau_bo *bo,
+                        struct nouveau_fence *fence, uint32_t flags)
+{
+       struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+       struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+       struct nouveau_resource *r;
 
-       if (on_list) {
-               chan->buffers[i].flags &= (flags | NOUVEAU_BO_RDWR);
-               chan->buffers[i].flags |= (flags & NOUVEAU_BO_RDWR);
-       } else {
-               chan->buffers[i].bo = bo;
-               chan->buffers[i].flags = flags;
-               chan->nr_buffers++;
-       }
+       if (nvchan->user_charge + bo->size > nvdev->sa.size)
+               return 1;
+       nvchan->user_charge += bo->size;
+
+       if (!(flags & NOUVEAU_BO_GART))
+               return 1;
+
+       r = nouveau_bo_tmp(chan, bo->size, fence);
+       if (!r)
+               return 1;
 
-       if (chan->num_relocs >= chan->max_relocs)
-               FIRE_RING_CH(userchan);
-       r = &chan->relocs[chan->num_relocs++];
+       memcpy(nvdev->sa_map + r->start, nvbo->sysmem, bo->size);
 
-       r->ptr = ptr;
-       r->bo = bo;
-       r->data = data;
-       r->flags = flags;
-       r->vor = vor;
-       r->tor = tor;
+       nvbo->offset = nvdev->sa.offset + r->start;
+       nvbo->flags = NOUVEAU_BO_GART;
+       return 0;
 }
 
 static int
-nouveau_bo_upload(struct nouveau_bo_priv *bo)
+nouveau_bo_validate_bo(struct nouveau_channel *chan, struct nouveau_bo *bo,
+                      struct nouveau_fence *fence, uint32_t flags)
 {
-       memcpy(bo->map, bo->sysmem, bo->drm.size);
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+
+       if (!nvbo->drm.size) {
+               nouveau_bo_realloc_gpu(nvbo, flags, nvbo->base.size);
+               nouveau_bo_upload(nvbo);
+               if (!nvbo->user) {
+                       free(nvbo->sysmem);
+                       nvbo->sysmem = NULL;
+               }
+       } else
+       if (nvbo->user) {
+               nouveau_bo_upload(nvbo);
+       }
+
+       nvbo->offset = nvbo->drm.offset;
+       if (nvbo->drm.flags & (NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI))
+               nvbo->flags = NOUVEAU_BO_GART;
+       else
+               nvbo->flags = NOUVEAU_BO_VRAM;
+
        return 0;
 }
 
-void
-nouveau_bo_validate(struct nouveau_channel *userchan)
+int
+nouveau_bo_validate(struct nouveau_channel *chan, struct nouveau_bo *bo,
+                   struct nouveau_fence *fence, uint32_t flags)
 {
-       struct nouveau_channel_priv *chan = nouveau_channel(userchan);
-       int i;
-
-       for (i = 0; i < chan->nr_buffers; i++) {
-               struct nouveau_bo_priv *bo = chan->buffers[i].bo;
-
-               if (!bo->drm.size) {
-                       nouveau_bo_realloc_gpu(bo, chan->buffers[i].flags,
-                                              bo->base.size);
-                       nouveau_bo_upload(bo);
-               } else
-               if (bo->user || bo->base.map)
-                       nouveau_bo_upload(bo);
-
-               if (!bo->user && !bo->base.map) {
-                       free(bo->sysmem);
-                       bo->sysmem = NULL;
-               }
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+       int ret;
 
+       assert(bo->map == NULL);
 
-               bo->base.offset = bo->drm.offset;
-               if (bo->drm.flags & NOUVEAU_MEM_AGP)
-                       bo->base.flags = NOUVEAU_BO_GART;
-               else
-                       bo->base.flags = NOUVEAU_BO_VRAM;
+       if (nvbo->user) {
+               ret = nouveau_bo_validate_user(chan, bo, fence, flags);
+               if (ret) {
+                       ret = nouveau_bo_validate_bo(chan, bo, fence, flags);
+                       if (ret)
+                               return ret;
+               }
+       } else {
+               ret = nouveau_bo_validate_bo(chan, bo, fence, flags);
+               if (ret)
+                       return ret;
        }
-       chan->nr_buffers = 0;
+
+       if (flags & NOUVEAU_BO_WR)
+               nouveau_fence_ref(fence, &nvbo->wr_fence);
+       nouveau_fence_ref(fence, &nvbo->fence);
+       return 0;
 }