Home | History | Annotate | Download | only in nine
      1 /**************************************************************************
      2  *
      3  * Copyright 2009 VMware, Inc.
      4  * Copyright 2016 Axel Davy <axel.davy (at) ens.fr>
      5  * All Rights Reserved.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a
      8  * copy of this software and associated documentation files (the
      9  * "Software"), to deal in the Software without restriction, including
     10  * without limitation the rights to use, copy, modify, merge, publish,
     11  * distribute, sub license, and/or sell copies of the Software, and to
     12  * permit persons to whom the Software is furnished to do so, subject to
     13  * the following conditions:
     14  *
     15  * The above copyright notice and this permission notice (including the
     16  * next paragraph) shall be included in all copies or substantial portions
     17  * of the Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     22  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     26  *
     27  **************************************************************************/
     28 /* Adapted from u_upload_mgr.
     29  * Makes suballocations from bigger allocations,
     30  * while enabling fast mapping. */
     31 
     32 #include "pipe/p_defines.h"
     33 #include "util/u_inlines.h"
     34 #include "pipe/p_context.h"
     35 #include "util/u_memory.h"
     36 #include "util/u_math.h"
     37 #include "util/slab.h"
     38 
     39 #include "nine_buffer_upload.h"
     40 
     41 #include "nine_debug.h"
     42 
     43 #define DBG_CHANNEL (DBG_INDEXBUFFER|DBG_VERTEXBUFFER)
     44 
     45 struct nine_buffer_group {
     46     unsigned refcount; /* How many sub-buffers live inside the buffer */
     47     struct pipe_resource *resource;
     48     struct pipe_transfer *transfer;
     49     uint8_t *map;
     50     unsigned free_offset; /* Aligned offset to the upload buffer, pointing
     51                            * at the first unused byte. */
     52 };
     53 
     54 struct nine_subbuffer {
     55     struct nine_buffer_group *parent; /* Can be NULL */
     56     struct pipe_resource *resource; /* The parent resource if apply */
     57     unsigned offset; /* Offset inside the resource */
     58     /* If there is no parent, the resource map. Else NULL. */
     59     struct pipe_transfer *transfer;
     60     uint8_t *map;
     61 };
     62 
     63 struct nine_buffer_upload {
     64     struct pipe_context *pipe;
     65     struct slab_mempool buffer_pool;
     66 
     67     unsigned buffers_size; /* Size of the big allocated buffers */
     68     unsigned num_buffers;
     69     struct nine_buffer_group *buffers;
     70 };
     71 
     72 static void
     73 nine_upload_create_buffer_group(struct nine_buffer_upload *upload,
     74                                 struct nine_buffer_group *group)
     75 {
     76     struct pipe_resource resource;
     77     struct pipe_screen *screen = upload->pipe->screen;
     78     DBG("%p %p\n", upload, group);
     79 
     80     memset(&resource, 0, sizeof(resource));
     81     resource.target = PIPE_BUFFER;
     82     resource.format = PIPE_FORMAT_R8_UNORM;
     83     resource.bind = PIPE_BIND_VERTEX_BUFFER;
     84     resource.usage = PIPE_USAGE_STREAM;
     85     resource.width0 = upload->buffers_size;
     86     resource.height0 = 1;
     87     resource.depth0 = 1;
     88     resource.array_size = 1;
     89     resource.flags = PIPE_RESOURCE_FLAG_MAP_PERSISTENT |
     90                      PIPE_RESOURCE_FLAG_MAP_COHERENT;
     91 
     92     group->refcount = 0;
     93     group->resource = screen->resource_create(screen, &resource);
     94     if (group->resource == NULL)
     95         return;
     96 
     97     group->map = pipe_buffer_map_range(upload->pipe, group->resource,
     98                                        0, upload->buffers_size,
     99                                        PIPE_TRANSFER_WRITE |
    100                                        PIPE_TRANSFER_PERSISTENT |
    101                                        PIPE_TRANSFER_COHERENT,
    102                                        &group->transfer);
    103     if (group->map == NULL) {
    104         group->transfer = NULL;
    105         pipe_resource_reference(&group->resource, NULL);
    106         return;
    107     }
    108 
    109     group->free_offset = 0;
    110 }
    111 
    112 static void
    113 nine_upload_destroy_buffer_group(struct nine_buffer_upload *upload,
    114                                  struct nine_buffer_group *group)
    115 {
    116     DBG("%p %p\n", upload, group);
    117     assert(group->refcount == 0);
    118 
    119     if (group->transfer)
    120         pipe_transfer_unmap(upload->pipe, group->transfer);
    121     if (group->resource)
    122         pipe_resource_reference(&group->resource, NULL);
    123     group->transfer = NULL;
    124     group->map = NULL;
    125 }
    126 
    127 struct nine_buffer_upload *
    128 nine_upload_create(struct pipe_context *pipe, unsigned buffers_size,
    129                    unsigned num_buffers)
    130 {
    131     struct nine_buffer_upload *upload;
    132     int i;
    133 
    134     DBG("\n");
    135 
    136     if (!pipe->screen->get_param(pipe->screen,
    137                                  PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT))
    138         return NULL;
    139 
    140     upload = CALLOC_STRUCT(nine_buffer_upload);
    141 
    142     if (!upload)
    143         return NULL;
    144 
    145     slab_create(&upload->buffer_pool, sizeof(struct nine_subbuffer), 4096);
    146 
    147     upload->pipe = pipe;
    148     upload->buffers_size = align(buffers_size, 4096);
    149     upload->num_buffers = num_buffers;
    150 
    151     upload->buffers = CALLOC(num_buffers, sizeof(struct nine_buffer_group));
    152     if (!upload->buffers)
    153         goto buffers_fail;
    154 
    155     for (i = 0; i < num_buffers; i++)
    156         nine_upload_create_buffer_group(upload, &upload->buffers[i]);
    157 
    158     return upload;
    159 
    160 buffers_fail:
    161     slab_destroy(&upload->buffer_pool);
    162     FREE(upload);
    163     return NULL;
    164 }
    165 
    166 void
    167 nine_upload_destroy(struct nine_buffer_upload *upload)
    168 {
    169     int i;
    170 
    171     DBG("%p\n", upload);
    172 
    173     for (i = 0; i < upload->num_buffers; i++)
    174         nine_upload_destroy_buffer_group(upload, &upload->buffers[i]);
    175     slab_destroy(&upload->buffer_pool);
    176     FREE(upload);
    177 }
    178 
    179 struct nine_subbuffer *
    180 nine_upload_create_buffer(struct nine_buffer_upload *upload,
    181                           unsigned buffer_size)
    182 {
    183     struct nine_subbuffer *buf = slab_alloc_st(&upload->buffer_pool);
    184     struct nine_buffer_group *group = NULL;
    185     unsigned size = align(buffer_size, 4096);
    186     int i = 0;
    187 
    188     DBG("%p %d\n", upload, buffer_size);
    189 
    190     if (!buf)
    191         return NULL;
    192 
    193     for (i = 0; i < upload->num_buffers; i++) {
    194         group = &upload->buffers[i];
    195         if (group->resource &&
    196             group->free_offset + size <= upload->buffers_size)
    197             break;
    198     }
    199 
    200     if (i == upload->num_buffers) {
    201         /* Allocate lonely buffer */
    202         struct pipe_resource resource;
    203         struct pipe_screen *screen = upload->pipe->screen;
    204 
    205         DBG("Allocating buffer\n");
    206         buf->parent = NULL;
    207 
    208         memset(&resource, 0, sizeof(resource));
    209         resource.target = PIPE_BUFFER;
    210         resource.format = PIPE_FORMAT_R8_UNORM;
    211         resource.bind = PIPE_BIND_VERTEX_BUFFER;
    212         resource.usage = PIPE_USAGE_STREAM;
    213         resource.width0 = buffer_size;
    214         resource.height0 = 1;
    215         resource.depth0 = 1;
    216         resource.array_size = 1;
    217         resource.flags = PIPE_RESOURCE_FLAG_MAP_PERSISTENT |
    218                          PIPE_RESOURCE_FLAG_MAP_COHERENT;
    219 
    220         buf->resource = screen->resource_create(screen, &resource);
    221         if (buf->resource == NULL) {
    222             slab_free_st(&upload->buffer_pool, buf);
    223             return NULL;
    224         }
    225 
    226         buf->map = pipe_buffer_map_range(upload->pipe, buf->resource,
    227                                          0, buffer_size,
    228                                          PIPE_TRANSFER_WRITE |
    229                                          PIPE_TRANSFER_PERSISTENT |
    230                                          PIPE_TRANSFER_COHERENT,
    231                                          &buf->transfer);
    232         if (buf->map == NULL) {
    233             pipe_resource_reference(&buf->resource, NULL);
    234             slab_free_st(&upload->buffer_pool, buf);
    235             return NULL;
    236         }
    237         buf->offset = 0;
    238         return buf;
    239     }
    240 
    241     DBG("Using buffer group %d\n", i);
    242 
    243     buf->parent = group;
    244     buf->resource = NULL;
    245     pipe_resource_reference(&buf->resource, group->resource);
    246     buf->offset = group->free_offset;
    247 
    248     group->free_offset += size;
    249     group->refcount += 1;
    250 
    251     return buf;
    252 }
    253 
    254 void
    255 nine_upload_release_buffer(struct nine_buffer_upload *upload,
    256                            struct nine_subbuffer *buf)
    257 {
    258     DBG("%p %p %p\n", upload, buf, buf->parent);
    259 
    260     if (buf->parent) {
    261         pipe_resource_reference(&buf->resource, NULL);
    262         buf->parent->refcount--;
    263         if (buf->parent->refcount == 0) {
    264             /* Allocate new buffer */
    265             nine_upload_destroy_buffer_group(upload, buf->parent);
    266             nine_upload_create_buffer_group(upload, buf->parent);
    267         }
    268     } else {
    269         /* lonely buffer */
    270         if (buf->transfer)
    271             pipe_transfer_unmap(upload->pipe, buf->transfer);
    272         pipe_resource_reference(&buf->resource, NULL);
    273     }
    274 
    275     slab_free_st(&upload->buffer_pool, buf);
    276 }
    277 
    278 uint8_t *
    279 nine_upload_buffer_get_map(struct nine_subbuffer *buf)
    280 {
    281     if (buf->parent) {
    282         return buf->parent->map + buf->offset;
    283     }
    284     /* lonely buffer */
    285     return buf->map;
    286 }
    287 
    288 struct pipe_resource *
    289 nine_upload_buffer_resource_and_offset(struct nine_subbuffer *buf,
    290                                        unsigned *offset)
    291 {
    292     *offset = buf->offset;
    293     return buf->resource;
    294 }
    295