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