Home | History | Annotate | Line # | Download | only in vulkan
      1 /*
      2  *  Copyright 2018 Alyssa Rosenzweig
      3  * Copyright (C) 2019 Collabora, Ltd.
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a
      6  * copy of this software and associated documentation files (the "Software"),
      7  * to deal in the Software without restriction, including without limitation
      8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      9  * and/or sell copies of the Software, and to permit persons to whom the
     10  * Software is furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice (including the next
     13  * paragraph) shall be included in all copies or substantial portions of the
     14  * Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22  * SOFTWARE.
     23  *
     24  */
     25 
     26 #include "pan_device.h"
     27 #include "panvk_mempool.h"
     28 
     29 /* Knockoff u_upload_mgr. Uploads wherever we left off, allocating new entries
     30  * when needed.
     31  *
     32  * In "owned" mode, a single parent owns the entire pool, and the pool owns all
     33  * created BOs. All BOs are tracked and addable as
     34  * panvk_pool_get_bo_handles. Freeing occurs at the level of an entire pool.
     35  * This is useful for streaming uploads, where the batch owns the pool.
     36  *
     37  * In "unowned" mode, the pool is freestanding. It does not track created BOs
     38  * or hold references. Instead, the consumer must manage the created BOs. This
     39  * is more flexible, enabling non-transient CSO state or shader code to be
     40  * packed with conservative lifetime handling.
     41  */
     42 
     43 static struct panfrost_bo *
     44 panvk_pool_alloc_backing(struct panvk_pool *pool, size_t bo_sz)
     45 {
     46    struct panfrost_bo *bo;
     47 
     48    /* If there's a free BO in our BO pool, let's pick it. */
     49    if (pool->bo_pool && bo_sz == pool->base.slab_size &&
     50        util_dynarray_num_elements(&pool->bo_pool->free_bos, struct panfrost_bo *)) {
     51       bo = util_dynarray_pop(&pool->bo_pool->free_bos, struct panfrost_bo *);
     52    } else {
     53       /* We don't know what the BO will be used for, so let's flag it
     54        * RW and attach it to both the fragment and vertex/tiler jobs.
     55        * TODO: if we want fine grained BO assignment we should pass
     56        * flags to this function and keep the read/write,
     57        * fragment/vertex+tiler pools separate.
     58        */
     59       bo = panfrost_bo_create(pool->base.dev, bo_sz,
     60                               pool->base.create_flags,
     61                               pool->base.label);
     62    }
     63 
     64    if (bo->size == pool->base.slab_size)
     65       util_dynarray_append(&pool->bos, struct panfrost_bo *, bo);
     66    else
     67       util_dynarray_append(&pool->big_bos, struct panfrost_bo *, bo);
     68    pool->transient_bo = bo;
     69    pool->transient_offset = 0;
     70 
     71    return bo;
     72 }
     73 
     74 static struct panfrost_ptr
     75 panvk_pool_alloc_aligned(struct panvk_pool *pool, size_t sz, unsigned alignment)
     76 {
     77    assert(alignment == util_next_power_of_two(alignment));
     78 
     79    /* Find or create a suitable BO */
     80    struct panfrost_bo *bo = pool->transient_bo;
     81    unsigned offset = ALIGN_POT(pool->transient_offset, alignment);
     82 
     83    /* If we don't fit, allocate a new backing */
     84    if (unlikely(bo == NULL || (offset + sz) >= pool->base.slab_size)) {
     85       bo = panvk_pool_alloc_backing(pool,
     86                                     ALIGN_POT(MAX2(pool->base.slab_size, sz),
     87                                     4096));
     88       offset = 0;
     89    }
     90 
     91    pool->transient_offset = offset + sz;
     92 
     93    struct panfrost_ptr ret = {
     94       .cpu = bo->ptr.cpu + offset,
     95       .gpu = bo->ptr.gpu + offset,
     96    };
     97 
     98    return ret;
     99 }
    100 PAN_POOL_ALLOCATOR(struct panvk_pool, panvk_pool_alloc_aligned)
    101 
    102 void
    103 panvk_pool_init(struct panvk_pool *pool,
    104                 struct panfrost_device *dev, struct panvk_bo_pool *bo_pool,
    105                 unsigned create_flags, size_t slab_size, const char *label,
    106                 bool prealloc)
    107 {
    108    memset(pool, 0, sizeof(*pool));
    109    pan_pool_init(&pool->base, dev, create_flags, slab_size, label);
    110    pool->bo_pool = bo_pool;
    111 
    112    util_dynarray_init(&pool->bos, NULL);
    113    util_dynarray_init(&pool->big_bos, NULL);
    114 
    115    if (prealloc)
    116       panvk_pool_alloc_backing(pool, pool->base.slab_size);
    117 }
    118 
    119 void
    120 panvk_pool_reset(struct panvk_pool *pool)
    121 {
    122    if (pool->bo_pool) {
    123       unsigned num_bos = panvk_pool_num_bos(pool);
    124       void *ptr = util_dynarray_grow(&pool->bo_pool->free_bos,
    125                                      struct panfrost_bo *, num_bos);
    126       memcpy(ptr, util_dynarray_begin(&pool->bos),
    127              num_bos * sizeof(struct panfrost_bo *));
    128    } else {
    129       util_dynarray_foreach(&pool->bos, struct panfrost_bo *, bo)
    130          panfrost_bo_unreference(*bo);
    131    }
    132 
    133    util_dynarray_foreach(&pool->big_bos, struct panfrost_bo *, bo)
    134       panfrost_bo_unreference(*bo);
    135 
    136    util_dynarray_clear(&pool->bos);
    137    util_dynarray_clear(&pool->big_bos);
    138    pool->transient_bo = NULL;
    139 }
    140 
    141 void
    142 panvk_pool_cleanup(struct panvk_pool *pool)
    143 {
    144    panvk_pool_reset(pool);
    145    util_dynarray_fini(&pool->bos);
    146    util_dynarray_fini(&pool->big_bos);
    147 }
    148 
    149 void
    150 panvk_pool_get_bo_handles(struct panvk_pool *pool, uint32_t *handles)
    151 {
    152    unsigned idx = 0;
    153    util_dynarray_foreach(&pool->bos, struct panfrost_bo *, bo) {
    154       assert((*bo)->gem_handle > 0);
    155       handles[idx++] = (*bo)->gem_handle;
    156    }
    157 }
    158