• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
util_translate_prim_restart_ib(struct pipe_context * context,struct pipe_index_buffer * src_buffer,struct pipe_resource ** dst_buffer,unsigned num_indexes,unsigned restart_index)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
add_range(struct range_info * info,unsigned start,unsigned count)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
util_draw_vbo_without_prim_restart(struct pipe_context * context,const struct pipe_index_buffer * ib,const struct pipe_draw_info * info)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