Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright 2014 VMware, Inc.
      3  * All Rights Reserved.
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a
      6  * copy of this software and associated documentation files (the
      7  * "Software"), to deal in the Software without restriction, including
      8  * without limitation the rights to use, copy, modify, merge, publish,
      9  * distribute, sub license, and/or sell copies of the Software, and to
     10  * permit persons to whom the Software is furnished to do so, subject to
     11  * the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the
     14  * next paragraph) shall be included in all copies or substantial portions
     15  * of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     20  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     21  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     24  */
     25 
     26 
     27 
     28 #include "u_inlines.h"
     29 #include "u_memory.h"
     30 #include "u_prim_restart.h"
     31 
     32 
     33 /**
     34  * Translate an index buffer for primitive restart.
     35  * Create a new index buffer which is a copy of the original index buffer
     36  * except that instances of 'restart_index' are converted to 0xffff or
     37  * 0xffffffff.
     38  * Also, index buffers using 1-byte indexes are converted to 2-byte indexes.
     39  */
     40 enum pipe_error
     41 util_translate_prim_restart_ib(struct pipe_context *context,
     42                                struct pipe_index_buffer *src_buffer,
     43                                struct pipe_resource **dst_buffer,
     44                                unsigned num_indexes,
     45                                unsigned restart_index)
     46 {
     47    struct pipe_screen *screen = context->screen;
     48    struct pipe_transfer *src_transfer = NULL, *dst_transfer = NULL;
     49    void *src_map = NULL, *dst_map = NULL;
     50    const unsigned src_index_size = src_buffer->index_size;
     51    unsigned dst_index_size;
     52 
     53    /* 1-byte indexes are converted to 2-byte indexes, 4-byte stays 4-byte */
     54    dst_index_size = MAX2(2, src_buffer->index_size);
     55    assert(dst_index_size == 2 || dst_index_size == 4);
     56 
     57    /* no user buffers for now */
     58    assert(src_buffer->user_buffer == NULL);
     59 
     60    /* Create new index buffer */
     61    *dst_buffer = pipe_buffer_create(screen, PIPE_BIND_INDEX_BUFFER,
     62                                     PIPE_USAGE_STREAM,
     63                                     num_indexes * dst_index_size);
     64    if (!*dst_buffer)
     65       goto error;
     66 
     67    /* Map new / dest index buffer */
     68    dst_map = pipe_buffer_map(context, *dst_buffer,
     69                              PIPE_TRANSFER_WRITE, &dst_transfer);
     70    if (!dst_map)
     71       goto error;
     72 
     73    /* Map original / src index buffer */
     74    src_map = pipe_buffer_map_range(context, src_buffer->buffer,
     75                                    src_buffer->offset,
     76                                    num_indexes * src_index_size,
     77                                    PIPE_TRANSFER_READ,
     78                                    &src_transfer);
     79    if (!src_map)
     80       goto error;
     81 
     82    if (src_index_size == 1 && dst_index_size == 2) {
     83       uint8_t *src = (uint8_t *) src_map;
     84       uint16_t *dst = (uint16_t *) dst_map;
     85       unsigned i;
     86       for (i = 0; i < num_indexes; i++) {
     87          dst[i] = (src[i] == restart_index) ? 0xffff : src[i];
     88       }
     89    }
     90    else if (src_index_size == 2 && dst_index_size == 2) {
     91       uint16_t *src = (uint16_t *) src_map;
     92       uint16_t *dst = (uint16_t *) dst_map;
     93       unsigned i;
     94       for (i = 0; i < num_indexes; i++) {
     95          dst[i] = (src[i] == restart_index) ? 0xffff : src[i];
     96       }
     97    }
     98    else {
     99       uint32_t *src = (uint32_t *) src_map;
    100       uint32_t *dst = (uint32_t *) dst_map;
    101       unsigned i;
    102       assert(src_index_size == 4);
    103       assert(dst_index_size == 4);
    104       for (i = 0; i < num_indexes; i++) {
    105          dst[i] = (src[i] == restart_index) ? 0xffffffff : src[i];
    106       }
    107    }
    108 
    109    pipe_buffer_unmap(context, src_transfer);
    110    pipe_buffer_unmap(context, dst_transfer);
    111 
    112    return PIPE_OK;
    113 
    114 error:
    115    if (src_transfer)
    116       pipe_buffer_unmap(context, src_transfer);
    117    if (dst_transfer)
    118       pipe_buffer_unmap(context, dst_transfer);
    119    if (*dst_buffer)
    120       screen->resource_destroy(screen, *dst_buffer);
    121    return PIPE_ERROR_OUT_OF_MEMORY;
    122 }
    123 
    124 
    125 /** Helper structs for util_draw_vbo_without_prim_restart() */
    126 
    127 struct range {
    128    unsigned start, count;
    129 };
    130 
    131 struct range_info {
    132    struct range *ranges;
    133    unsigned count, max;
    134 };
    135 
    136 
    137 /**
    138  * Helper function for util_draw_vbo_without_prim_restart()
    139  * \return true for success, false if out of memory
    140  */
    141 static boolean
    142 add_range(struct range_info *info, unsigned start, unsigned count)
    143 {
    144    if (info->max == 0) {
    145       info->max = 10;
    146       info->ranges = MALLOC(info->max * sizeof(struct range));
    147       if (!info->ranges) {
    148          return FALSE;
    149       }
    150    }
    151    else if (info->count == info->max) {
    152       /* grow the ranges[] array */
    153       info->ranges = REALLOC(info->ranges,
    154                              info->max * sizeof(struct range),
    155                              2 * info->max * sizeof(struct range));
    156       if (!info->ranges) {
    157          return FALSE;
    158       }
    159 
    160       info->max *= 2;
    161    }
    162 
    163    /* save the range */
    164    info->ranges[info->count].start = start;
    165    info->ranges[info->count].count = count;
    166    info->count++;
    167 
    168    return TRUE;
    169 }
    170 
    171 
    172 /**
    173  * Implement primitive restart by breaking an indexed primitive into
    174  * pieces which do not contain restart indexes.  Each piece is then
    175  * drawn by calling pipe_context::draw_vbo().
    176  * \return PIPE_OK if no error, an error code otherwise.
    177  */
    178 enum pipe_error
    179 util_draw_vbo_without_prim_restart(struct pipe_context *context,
    180                                    const struct pipe_index_buffer *ib,
    181                                    const struct pipe_draw_info *info)
    182 {
    183    const void *src_map;
    184    struct range_info ranges = {0};
    185    struct pipe_draw_info new_info;
    186    struct pipe_transfer *src_transfer = NULL;
    187    unsigned i, start, count;
    188 
    189    assert(info->indexed);
    190    assert(info->primitive_restart);
    191 
    192    /* Get pointer to the index data */
    193    if (ib->buffer) {
    194       /* map the index buffer (only the range we need to scan) */
    195       src_map = pipe_buffer_map_range(context, ib->buffer,
    196                                       ib->offset + info->start * ib->index_size,
    197                                       info->count * ib->index_size,
    198                                       PIPE_TRANSFER_READ,
    199                                       &src_transfer);
    200       if (!src_map) {
    201          return PIPE_ERROR_OUT_OF_MEMORY;
    202       }
    203    }
    204    else {
    205       if (!ib->user_buffer) {
    206          debug_printf("User-space index buffer is null!");
    207          return PIPE_ERROR_BAD_INPUT;
    208       }
    209       src_map = (const uint8_t *) ib->user_buffer
    210          + ib->offset
    211          + info->start * ib->index_size;
    212    }
    213 
    214 #define SCAN_INDEXES(TYPE) \
    215    for (i = 0; i <= info->count; i++) { \
    216       if (i == info->count || \
    217           ((const TYPE *) src_map)[i] == info->restart_index) { \
    218          /* cut / restart */ \
    219          if (count > 0) { \
    220             if (!add_range(&ranges, info->start + start, count)) { \
    221                if (src_transfer) \
    222                   pipe_buffer_unmap(context, src_transfer); \
    223                return PIPE_ERROR_OUT_OF_MEMORY; \
    224             } \
    225          } \
    226          start = i + 1; \
    227          count = 0; \
    228       } \
    229       else { \
    230          count++; \
    231       } \
    232    }
    233 
    234    start = info->start;
    235    count = 0;
    236    switch (ib->index_size) {
    237    case 1:
    238       SCAN_INDEXES(uint8_t);
    239       break;
    240    case 2:
    241       SCAN_INDEXES(uint16_t);
    242       break;
    243    case 4:
    244       SCAN_INDEXES(uint32_t);
    245       break;
    246    default:
    247       assert(!"Bad index size");
    248       return PIPE_ERROR_BAD_INPUT;
    249    }
    250 
    251    /* unmap index buffer */
    252    if (src_transfer)
    253       pipe_buffer_unmap(context, src_transfer);
    254 
    255    /* draw ranges between the restart indexes */
    256    new_info = *info;
    257    new_info.primitive_restart = FALSE;
    258    for (i = 0; i < ranges.count; i++) {
    259       new_info.start = ranges.ranges[i].start;
    260       new_info.count = ranges.ranges[i].count;
    261       context->draw_vbo(context, &new_info);
    262    }
    263 
    264    FREE(ranges.ranges);
    265 
    266    return PIPE_OK;
    267 }
    268