Home | History | Annotate | Download | only in util
      1 /**************************************************************************
      2  *
      3  * Copyright 2009 VMware, Inc.
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 /* Helper utility for uploading user buffers & other data, and
     29  * coalescing small buffers into larger ones.
     30  */
     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 
     38 #include "u_upload_mgr.h"
     39 
     40 
     41 struct u_upload_mgr {
     42    struct pipe_context *pipe;
     43 
     44    unsigned default_size;  /* Minimum size of the upload buffer, in bytes. */
     45    unsigned alignment;     /* Alignment of each sub-allocation. */
     46    unsigned bind;          /* Bitmask of PIPE_BIND_* flags. */
     47 
     48    struct pipe_resource *buffer;   /* Upload buffer. */
     49    struct pipe_transfer *transfer; /* Transfer object for the upload buffer. */
     50    uint8_t *map;    /* Pointer to the mapped upload buffer. */
     51    unsigned size;   /* Actual size of the upload buffer. */
     52    unsigned offset; /* Aligned offset to the upload buffer, pointing
     53                      * at the first unused byte. */
     54 };
     55 
     56 
     57 struct u_upload_mgr *u_upload_create( struct pipe_context *pipe,
     58                                       unsigned default_size,
     59                                       unsigned alignment,
     60                                       unsigned bind )
     61 {
     62    struct u_upload_mgr *upload = CALLOC_STRUCT( u_upload_mgr );
     63    if (!upload)
     64       return NULL;
     65 
     66    upload->pipe = pipe;
     67    upload->default_size = default_size;
     68    upload->alignment = alignment;
     69    upload->bind = bind;
     70    upload->buffer = NULL;
     71 
     72    return upload;
     73 }
     74 
     75 void u_upload_unmap( struct u_upload_mgr *upload )
     76 {
     77    if (upload->transfer) {
     78       struct pipe_box *box = &upload->transfer->box;
     79       if (upload->offset > box->x) {
     80 
     81          pipe_buffer_flush_mapped_range(upload->pipe, upload->transfer,
     82                                         box->x, upload->offset - box->x);
     83       }
     84       pipe_transfer_unmap(upload->pipe, upload->transfer);
     85       pipe_transfer_destroy(upload->pipe, upload->transfer);
     86       upload->transfer = NULL;
     87       upload->map = NULL;
     88    }
     89 }
     90 
     91 /* Release old buffer.
     92  *
     93  * This must usually be called prior to firing the command stream
     94  * which references the upload buffer, as many memory managers will
     95  * cause subsequent maps of a fired buffer to wait.
     96  *
     97  * Can improve this with a change to pipe_buffer_write to use the
     98  * DONT_WAIT bit, but for now, it's easiest just to grab a new buffer.
     99  */
    100 void u_upload_flush( struct u_upload_mgr *upload )
    101 {
    102    /* Unmap and unreference the upload buffer. */
    103    u_upload_unmap(upload);
    104    pipe_resource_reference( &upload->buffer, NULL );
    105    upload->size = 0;
    106 }
    107 
    108 
    109 void u_upload_destroy( struct u_upload_mgr *upload )
    110 {
    111    u_upload_flush( upload );
    112    FREE( upload );
    113 }
    114 
    115 
    116 static enum pipe_error
    117 u_upload_alloc_buffer( struct u_upload_mgr *upload,
    118                        unsigned min_size )
    119 {
    120    unsigned size;
    121 
    122    /* Release the old buffer, if present:
    123     */
    124    u_upload_flush( upload );
    125 
    126    /* Allocate a new one:
    127     */
    128    size = align(MAX2(upload->default_size, min_size), 4096);
    129 
    130    upload->buffer = pipe_buffer_create( upload->pipe->screen,
    131                                         upload->bind,
    132                                         PIPE_USAGE_STREAM,
    133                                         size );
    134    if (upload->buffer == NULL) {
    135       return PIPE_ERROR_OUT_OF_MEMORY;
    136    }
    137 
    138    /* Map the new buffer. */
    139    upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer,
    140                                        0, size,
    141                                        PIPE_TRANSFER_WRITE |
    142                                        PIPE_TRANSFER_FLUSH_EXPLICIT,
    143                                        &upload->transfer);
    144    if (upload->map == NULL) {
    145       upload->size = 0;
    146       pipe_resource_reference(&upload->buffer, NULL);
    147       return PIPE_ERROR_OUT_OF_MEMORY;
    148    }
    149 
    150    upload->size = size;
    151 
    152    upload->offset = 0;
    153    return PIPE_OK;
    154 }
    155 
    156 enum pipe_error u_upload_alloc( struct u_upload_mgr *upload,
    157                                 unsigned min_out_offset,
    158                                 unsigned size,
    159                                 unsigned *out_offset,
    160                                 struct pipe_resource **outbuf,
    161                                 void **ptr )
    162 {
    163    unsigned alloc_size = align( size, upload->alignment );
    164    unsigned alloc_offset = align(min_out_offset, upload->alignment);
    165    unsigned offset;
    166 
    167    /* Init these return values here in case we fail below to make
    168     * sure the caller doesn't get garbage values.
    169     */
    170    *out_offset = ~0;
    171    pipe_resource_reference(outbuf, NULL);
    172    *ptr = NULL;
    173 
    174    /* Make sure we have enough space in the upload buffer
    175     * for the sub-allocation. */
    176    if (MAX2(upload->offset, alloc_offset) + alloc_size > upload->size) {
    177       enum pipe_error ret = u_upload_alloc_buffer(upload,
    178                                                   alloc_offset + alloc_size);
    179       if (ret != PIPE_OK)
    180          return ret;
    181    }
    182 
    183    offset = MAX2(upload->offset, alloc_offset);
    184 
    185    if (!upload->map) {
    186       upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer,
    187 					  offset, upload->size - offset,
    188 					  PIPE_TRANSFER_WRITE |
    189 					  PIPE_TRANSFER_FLUSH_EXPLICIT |
    190 					  PIPE_TRANSFER_UNSYNCHRONIZED,
    191 					  &upload->transfer);
    192       if (!upload->map) {
    193          upload->transfer = NULL;
    194          return PIPE_ERROR_OUT_OF_MEMORY;
    195       }
    196 
    197       upload->map -= offset;
    198    }
    199 
    200    assert(offset < upload->buffer->width0);
    201    assert(offset + size <= upload->buffer->width0);
    202    assert(size);
    203 
    204    /* Emit the return values: */
    205    *ptr = upload->map + offset;
    206    pipe_resource_reference( outbuf, upload->buffer );
    207    *out_offset = offset;
    208 
    209    upload->offset = offset + alloc_size;
    210    return PIPE_OK;
    211 }
    212 
    213 enum pipe_error u_upload_data( struct u_upload_mgr *upload,
    214                                unsigned min_out_offset,
    215                                unsigned size,
    216                                const void *data,
    217                                unsigned *out_offset,
    218                                struct pipe_resource **outbuf)
    219 {
    220    uint8_t *ptr;
    221    enum pipe_error ret = u_upload_alloc(upload, min_out_offset, size,
    222                                         out_offset, outbuf,
    223                                         (void**)&ptr);
    224    if (ret != PIPE_OK)
    225       return ret;
    226 
    227    memcpy(ptr, data, size);
    228    return PIPE_OK;
    229 }
    230 
    231 
    232 /* As above, but upload the full contents of a buffer.  Useful for
    233  * uploading user buffers, avoids generating an explosion of GPU
    234  * buffers if you have an app that does lots of small vertex buffer
    235  * renders or DrawElements calls.
    236  */
    237 enum pipe_error u_upload_buffer( struct u_upload_mgr *upload,
    238                                  unsigned min_out_offset,
    239                                  unsigned offset,
    240                                  unsigned size,
    241                                  struct pipe_resource *inbuf,
    242                                  unsigned *out_offset,
    243                                  struct pipe_resource **outbuf)
    244 {
    245    enum pipe_error ret = PIPE_OK;
    246    struct pipe_transfer *transfer = NULL;
    247    const char *map = NULL;
    248 
    249    map = (const char *)pipe_buffer_map_range(upload->pipe,
    250                                              inbuf,
    251                                              offset, size,
    252                                              PIPE_TRANSFER_READ,
    253                                              &transfer);
    254 
    255    if (map == NULL) {
    256       return PIPE_ERROR_OUT_OF_MEMORY;
    257    }
    258 
    259    if (0)
    260       debug_printf("upload ptr %p ofs %d sz %d\n", map, offset, size);
    261 
    262    ret = u_upload_data( upload,
    263                         min_out_offset,
    264                         size,
    265                         map,
    266                         out_offset,
    267                         outbuf);
    268 
    269    pipe_buffer_unmap( upload->pipe, transfer );
    270 
    271    return ret;
    272 }
    273