anv/allocator: Correctly set the number of buckets
[mesa.git] / src / intel / vulkan / anv_allocator.c
index 4fc83386a71c0c6c4aeb549ca371752182ef2520..f268e721fa8f62d362d198f07237bc52ebcc97ee 100644 (file)
@@ -21,8 +21,6 @@
  * IN THE SOFTWARE.
  */
 
-#define _DEFAULT_SOURCE
-
 #include <stdint.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -794,12 +792,10 @@ struct bo_pool_bo_link {
 };
 
 void
-anv_bo_pool_init(struct anv_bo_pool *pool,
-                 struct anv_device *device, uint32_t bo_size)
+anv_bo_pool_init(struct anv_bo_pool *pool, struct anv_device *device)
 {
    pool->device = device;
-   pool->bo_size = bo_size;
-   pool->free_list = NULL;
+   memset(pool->free_list, 0, sizeof(pool->free_list));
 
    VG(VALGRIND_CREATE_MEMPOOL(pool, 0, false));
 }
@@ -807,44 +803,51 @@ anv_bo_pool_init(struct anv_bo_pool *pool,
 void
 anv_bo_pool_finish(struct anv_bo_pool *pool)
 {
-   struct bo_pool_bo_link *link = PFL_PTR(pool->free_list);
-   while (link != NULL) {
-      struct bo_pool_bo_link link_copy = VG_NOACCESS_READ(link);
-
-      anv_gem_munmap(link_copy.bo.map, pool->bo_size);
-      anv_gem_close(pool->device, link_copy.bo.gem_handle);
-      link = link_copy.next;
+   for (unsigned i = 0; i < ARRAY_SIZE(pool->free_list); i++) {
+      struct bo_pool_bo_link *link = PFL_PTR(pool->free_list[i]);
+      while (link != NULL) {
+         struct bo_pool_bo_link link_copy = VG_NOACCESS_READ(link);
+
+         anv_gem_munmap(link_copy.bo.map, link_copy.bo.size);
+         anv_gem_close(pool->device, link_copy.bo.gem_handle);
+         link = link_copy.next;
+      }
    }
 
    VG(VALGRIND_DESTROY_MEMPOOL(pool));
 }
 
 VkResult
-anv_bo_pool_alloc(struct anv_bo_pool *pool, struct anv_bo *bo)
+anv_bo_pool_alloc(struct anv_bo_pool *pool, struct anv_bo *bo, uint32_t size)
 {
    VkResult result;
 
+   const unsigned size_log2 = size < 4096 ? 12 : ilog2_round_up(size);
+   const unsigned pow2_size = 1 << size_log2;
+   const unsigned bucket = size_log2 - 12;
+   assert(bucket < ARRAY_SIZE(pool->free_list));
+
    void *next_free_void;
-   if (anv_ptr_free_list_pop(&pool->free_list, &next_free_void)) {
+   if (anv_ptr_free_list_pop(&pool->free_list[bucket], &next_free_void)) {
       struct bo_pool_bo_link *next_free = next_free_void;
       *bo = VG_NOACCESS_READ(&next_free->bo);
       assert(bo->map == next_free);
-      assert(bo->size == pool->bo_size);
+      assert(size <= bo->size);
 
-      VG(VALGRIND_MEMPOOL_ALLOC(pool, bo->map, pool->bo_size));
+      VG(VALGRIND_MEMPOOL_ALLOC(pool, bo->map, size));
 
       return VK_SUCCESS;
    }
 
    struct anv_bo new_bo;
 
-   result = anv_bo_init_new(&new_bo, pool->device, pool->bo_size);
+   result = anv_bo_init_new(&new_bo, pool->device, pow2_size);
    if (result != VK_SUCCESS)
       return result;
 
-   assert(new_bo.size == pool->bo_size);
+   assert(new_bo.size == pow2_size);
 
-   new_bo.map = anv_gem_mmap(pool->device, new_bo.gem_handle, 0, pool->bo_size, 0);
+   new_bo.map = anv_gem_mmap(pool->device, new_bo.gem_handle, 0, pow2_size, 0);
    if (new_bo.map == NULL) {
       anv_gem_close(pool->device, new_bo.gem_handle);
       return vk_error(VK_ERROR_MEMORY_MAP_FAILED);
@@ -852,7 +855,7 @@ anv_bo_pool_alloc(struct anv_bo_pool *pool, struct anv_bo *bo)
 
    *bo = new_bo;
 
-   VG(VALGRIND_MEMPOOL_ALLOC(pool, bo->map, pool->bo_size));
+   VG(VALGRIND_MEMPOOL_ALLOC(pool, bo->map, size));
 
    return VK_SUCCESS;
 }
@@ -865,6 +868,87 @@ anv_bo_pool_free(struct anv_bo_pool *pool, const struct anv_bo *bo_in)
    struct bo_pool_bo_link *link = bo.map;
    link->bo = bo;
 
+   assert(util_is_power_of_two(bo.size));
+   const unsigned size_log2 = ilog2_round_up(bo.size);
+   const unsigned bucket = size_log2 - 12;
+   assert(bucket < ARRAY_SIZE(pool->free_list));
+
    VG(VALGRIND_MEMPOOL_FREE(pool, bo.map));
-   anv_ptr_free_list_push(&pool->free_list, link);
+   anv_ptr_free_list_push(&pool->free_list[bucket], link);
+}
+
+// Scratch pool
+
+void
+anv_scratch_pool_init(struct anv_device *device, struct anv_scratch_pool *pool)
+{
+   memset(pool, 0, sizeof(*pool));
+}
+
+void
+anv_scratch_pool_finish(struct anv_device *device, struct anv_scratch_pool *pool)
+{
+   for (unsigned s = 0; s < MESA_SHADER_STAGES; s++) {
+      for (unsigned i = 0; i < 16; i++) {
+         struct anv_bo *bo = &pool->bos[i][s];
+         if (bo->size > 0)
+            anv_gem_close(device, bo->gem_handle);
+      }
+   }
+}
+
+struct anv_bo *
+anv_scratch_pool_alloc(struct anv_device *device, struct anv_scratch_pool *pool,
+                       gl_shader_stage stage, unsigned per_thread_scratch)
+{
+   if (per_thread_scratch == 0)
+      return NULL;
+
+   unsigned scratch_size_log2 = ffs(per_thread_scratch / 2048);
+   assert(scratch_size_log2 < 16);
+
+   struct anv_bo *bo = &pool->bos[scratch_size_log2][stage];
+
+   /* From now on, we go into a critical section.  In order to remain
+    * thread-safe, we use the bo size as a lock.  A value of 0 means we don't
+    * have a valid BO yet.  A value of 1 means locked.  A value greater than 1
+    * means we have a bo of the given size.
+    */
+
+   if (bo->size > 1)
+      return bo;
+
+   uint64_t size = __sync_val_compare_and_swap(&bo->size, 0, 1);
+   if (size == 0) {
+      /* We own the lock.  Allocate a buffer */
+
+      struct brw_device_info *devinfo = &device->info;
+      uint32_t max_threads[] = {
+         [MESA_SHADER_VERTEX]                  = devinfo->max_vs_threads,
+         [MESA_SHADER_TESS_CTRL]               = devinfo->max_hs_threads,
+         [MESA_SHADER_TESS_EVAL]               = devinfo->max_ds_threads,
+         [MESA_SHADER_GEOMETRY]                = devinfo->max_gs_threads,
+         [MESA_SHADER_FRAGMENT]                = devinfo->max_wm_threads,
+         [MESA_SHADER_COMPUTE]                 = devinfo->max_cs_threads,
+      };
+
+      size = per_thread_scratch * max_threads[stage];
+
+      struct anv_bo new_bo;
+      anv_bo_init_new(&new_bo, device, size);
+
+      bo->gem_handle = new_bo.gem_handle;
+
+      /* Set the size last because we use it as a lock */
+      __sync_synchronize();
+      bo->size = size;
+
+      futex_wake((uint32_t *)&bo->size, INT_MAX);
+   } else {
+      /* Someone else got here first */
+      while (bo->size == 1)
+         futex_wait((uint32_t *)&bo->size, 1);
+   }
+
+   return bo;
 }