• 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 "util/u_memory.h"
30 #include "u_prim_restart.h"
31 
32 typedef struct {
33   uint32_t count;
34   uint32_t primCount;
35   uint32_t firstIndex;
36   int32_t  baseVertex;
37   uint32_t reservedMustBeZero;
38 } DrawElementsIndirectCommand;
39 
40 static DrawElementsIndirectCommand
read_indirect_elements(struct pipe_context * context,struct pipe_draw_indirect_info * indirect)41 read_indirect_elements(struct pipe_context *context, struct pipe_draw_indirect_info *indirect)
42 {
43    DrawElementsIndirectCommand ret;
44    struct pipe_transfer *transfer = NULL;
45    void *map = NULL;
46    /* we only need the first 3 members */
47    unsigned read_size = 3 * sizeof(uint32_t);
48    assert(indirect->buffer->width0 > 3 * sizeof(uint32_t));
49    map = pipe_buffer_map_range(context, indirect->buffer,
50                                    indirect->offset,
51                                    read_size,
52                                    PIPE_MAP_READ,
53                                    &transfer);
54    assert(map);
55    memcpy(&ret, map, read_size);
56    pipe_buffer_unmap(context, transfer);
57    return ret;
58 }
59 
60 void
util_translate_prim_restart_data(unsigned index_size,void * src_map,void * dst_map,unsigned count,unsigned restart_index)61 util_translate_prim_restart_data(unsigned index_size,
62                                  void *src_map, void *dst_map,
63                                  unsigned count, unsigned restart_index)
64 {
65    if (index_size == 1) {
66       uint8_t *src = (uint8_t *) src_map;
67       uint16_t *dst = (uint16_t *) dst_map;
68       unsigned i;
69       for (i = 0; i < count; i++) {
70          dst[i] = (src[i] == restart_index) ? 0xffff : src[i];
71       }
72    }
73    else if (index_size == 2) {
74       uint16_t *src = (uint16_t *) src_map;
75       uint16_t *dst = (uint16_t *) dst_map;
76       unsigned i;
77       for (i = 0; i < count; i++) {
78          dst[i] = (src[i] == restart_index) ? 0xffff : src[i];
79       }
80    }
81    else {
82       uint32_t *src = (uint32_t *) src_map;
83       uint32_t *dst = (uint32_t *) dst_map;
84       unsigned i;
85       assert(index_size == 4);
86       for (i = 0; i < count; i++) {
87          dst[i] = (src[i] == restart_index) ? 0xffffffff : src[i];
88       }
89    }
90 }
91 
92 /**
93  * Translate an index buffer for primitive restart.
94  * Create a new index buffer which is a copy of the original index buffer
95  * except that instances of 'restart_index' are converted to 0xffff or
96  * 0xffffffff.
97  * Also, index buffers using 1-byte indexes are converted to 2-byte indexes.
98  */
99 enum pipe_error
util_translate_prim_restart_ib(struct pipe_context * context,const struct pipe_draw_info * info,struct pipe_resource ** dst_buffer)100 util_translate_prim_restart_ib(struct pipe_context *context,
101                                const struct pipe_draw_info *info,
102                                struct pipe_resource **dst_buffer)
103 {
104    struct pipe_screen *screen = context->screen;
105    struct pipe_transfer *src_transfer = NULL, *dst_transfer = NULL;
106    void *src_map = NULL, *dst_map = NULL;
107    const unsigned src_index_size = info->index_size;
108    unsigned dst_index_size;
109    DrawElementsIndirectCommand indirect;
110    unsigned count = info->count;
111    unsigned start = info->start;
112 
113    /* 1-byte indexes are converted to 2-byte indexes, 4-byte stays 4-byte */
114    dst_index_size = MAX2(2, info->index_size);
115    assert(dst_index_size == 2 || dst_index_size == 4);
116 
117    if (info->indirect) {
118       indirect = read_indirect_elements(context, info->indirect);
119       count = indirect.count;
120       start = indirect.firstIndex;
121    }
122 
123    /* Create new index buffer */
124    *dst_buffer = pipe_buffer_create(screen, PIPE_BIND_INDEX_BUFFER,
125                                     PIPE_USAGE_STREAM,
126                                     count * dst_index_size);
127    if (!*dst_buffer)
128       goto error;
129 
130    /* Map new / dest index buffer */
131    dst_map = pipe_buffer_map(context, *dst_buffer,
132                              PIPE_MAP_WRITE, &dst_transfer);
133    if (!dst_map)
134       goto error;
135 
136    if (info->has_user_indices)
137       src_map = (unsigned char*)info->index.user + start * src_index_size;
138    else
139       /* Map original / src index buffer */
140       src_map = pipe_buffer_map_range(context, info->index.resource,
141                                       start * src_index_size,
142                                       count * src_index_size,
143                                       PIPE_MAP_READ,
144                                       &src_transfer);
145    if (!src_map)
146       goto error;
147 
148    util_translate_prim_restart_data(src_index_size, src_map, dst_map,
149                                     info->count, info->restart_index);
150 
151    if (src_transfer)
152       pipe_buffer_unmap(context, src_transfer);
153    pipe_buffer_unmap(context, dst_transfer);
154 
155    return PIPE_OK;
156 
157 error:
158    if (src_transfer)
159       pipe_buffer_unmap(context, src_transfer);
160    if (dst_transfer)
161       pipe_buffer_unmap(context, dst_transfer);
162    if (*dst_buffer)
163       pipe_resource_reference(dst_buffer, NULL);
164    return PIPE_ERROR_OUT_OF_MEMORY;
165 }
166 
167 
168 /** Helper structs for util_draw_vbo_without_prim_restart() */
169 
170 struct range {
171    unsigned start, count;
172 };
173 
174 struct range_info {
175    struct range *ranges;
176    unsigned count, max;
177 };
178 
179 
180 /**
181  * Helper function for util_draw_vbo_without_prim_restart()
182  * \return true for success, false if out of memory
183  */
184 static boolean
add_range(struct range_info * info,unsigned start,unsigned count)185 add_range(struct range_info *info, unsigned start, unsigned count)
186 {
187    if (info->max == 0) {
188       info->max = 10;
189       info->ranges = MALLOC(info->max * sizeof(struct range));
190       if (!info->ranges) {
191          return FALSE;
192       }
193    }
194    else if (info->count == info->max) {
195       /* grow the ranges[] array */
196       info->ranges = REALLOC(info->ranges,
197                              info->max * sizeof(struct range),
198                              2 * info->max * sizeof(struct range));
199       if (!info->ranges) {
200          return FALSE;
201       }
202 
203       info->max *= 2;
204    }
205 
206    /* save the range */
207    info->ranges[info->count].start = start;
208    info->ranges[info->count].count = count;
209    info->count++;
210 
211    return TRUE;
212 }
213 
214 
215 /**
216  * Implement primitive restart by breaking an indexed primitive into
217  * pieces which do not contain restart indexes.  Each piece is then
218  * drawn by calling pipe_context::draw_vbo().
219  * \return PIPE_OK if no error, an error code otherwise.
220  */
221 enum pipe_error
util_draw_vbo_without_prim_restart(struct pipe_context * context,const struct pipe_draw_info * info)222 util_draw_vbo_without_prim_restart(struct pipe_context *context,
223                                    const struct pipe_draw_info *info)
224 {
225    const void *src_map;
226    struct range_info ranges = {0};
227    struct pipe_draw_info new_info;
228    struct pipe_transfer *src_transfer = NULL;
229    unsigned i, start, count;
230    DrawElementsIndirectCommand indirect;
231    unsigned info_start = info->start;
232    unsigned info_count = info->count;
233    unsigned info_instance_count = info->instance_count;
234 
235    assert(info->index_size);
236    assert(info->primitive_restart);
237 
238    if (info->indirect) {
239       indirect = read_indirect_elements(context, info->indirect);
240       info_count = indirect.count;
241       info_start = indirect.firstIndex;
242       info_instance_count = indirect.primCount;
243    }
244 
245    /* Get pointer to the index data */
246    if (!info->has_user_indices) {
247       /* map the index buffer (only the range we need to scan) */
248       src_map = pipe_buffer_map_range(context, info->index.resource,
249                                       info_start * info->index_size,
250                                       info_count * info->index_size,
251                                       PIPE_MAP_READ,
252                                       &src_transfer);
253       if (!src_map) {
254          return PIPE_ERROR_OUT_OF_MEMORY;
255       }
256    }
257    else {
258       if (!info->index.user) {
259          debug_printf("User-space index buffer is null!");
260          return PIPE_ERROR_BAD_INPUT;
261       }
262       src_map = (const uint8_t *) info->index.user
263          + info_start * info->index_size;
264    }
265 
266 #define SCAN_INDEXES(TYPE) \
267    for (i = 0; i <= info_count; i++) { \
268       if (i == info_count || \
269           ((const TYPE *) src_map)[i] == info->restart_index) { \
270          /* cut / restart */ \
271          if (count > 0) { \
272             if (!add_range(&ranges, info_start + start, count)) { \
273                if (src_transfer) \
274                   pipe_buffer_unmap(context, src_transfer); \
275                return PIPE_ERROR_OUT_OF_MEMORY; \
276             } \
277          } \
278          start = i + 1; \
279          count = 0; \
280       } \
281       else { \
282          count++; \
283       } \
284    }
285 
286    start = 0;
287    count = 0;
288    switch (info->index_size) {
289    case 1:
290       SCAN_INDEXES(uint8_t);
291       break;
292    case 2:
293       SCAN_INDEXES(uint16_t);
294       break;
295    case 4:
296       SCAN_INDEXES(uint32_t);
297       break;
298    default:
299       assert(!"Bad index size");
300       return PIPE_ERROR_BAD_INPUT;
301    }
302 
303    /* unmap index buffer */
304    if (src_transfer)
305       pipe_buffer_unmap(context, src_transfer);
306 
307    /* draw ranges between the restart indexes */
308    new_info = *info;
309    /* we've effectively remapped this to a direct draw */
310    new_info.indirect = NULL;
311    new_info.instance_count = info_instance_count;
312    new_info.primitive_restart = FALSE;
313    for (i = 0; i < ranges.count; i++) {
314       new_info.start = ranges.ranges[i].start;
315       new_info.count = ranges.ranges[i].count;
316       context->draw_vbo(context, &new_info);
317    }
318 
319    FREE(ranges.ranges);
320 
321    return PIPE_OK;
322 }
323