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 /** Helper structs for util_draw_vbo_without_prim_restart() */
94
95 struct range_info {
96 struct pipe_draw_start_count_bias *draws;
97 unsigned count, max;
98 unsigned min_index, max_index;
99 unsigned total_index_count;
100 };
101
102
103 /**
104 * Helper function for util_draw_vbo_without_prim_restart()
105 * \return true for success, false if out of memory
106 */
107 static bool
add_range(enum mesa_prim mode,struct range_info * info,unsigned start,unsigned count,unsigned index_bias)108 add_range(enum mesa_prim mode, struct range_info *info, unsigned start, unsigned count, unsigned index_bias)
109 {
110 /* degenerate primitive: ignore */
111 if (!u_trim_pipe_prim(mode, (unsigned*)&count))
112 return true;
113
114 if (info->max == 0) {
115 info->max = 10;
116 info->draws = MALLOC(info->max * sizeof(struct pipe_draw_start_count_bias));
117 if (!info->draws) {
118 return false;
119 }
120 }
121 else if (info->count == info->max) {
122 /* grow the draws[] array */
123 info->draws = REALLOC(info->draws,
124 info->max * sizeof(struct pipe_draw_start_count_bias),
125 2 * info->max * sizeof(struct pipe_draw_start_count_bias));
126 if (!info->draws) {
127 return false;
128 }
129
130 info->max *= 2;
131 }
132 info->min_index = MIN2(info->min_index, start);
133 info->max_index = MAX2(info->max_index, start + count - 1);
134
135 /* save the range */
136 info->draws[info->count].start = start;
137 info->draws[info->count].count = count;
138 info->draws[info->count].index_bias = index_bias;
139 info->count++;
140 info->total_index_count += count;
141
142 return true;
143 }
144
145 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)146 util_prim_restart_convert_to_direct(const void *index_map,
147 const struct pipe_draw_info *info,
148 const struct pipe_draw_start_count_bias *draw,
149 unsigned *num_draws,
150 unsigned *min_index,
151 unsigned *max_index,
152 unsigned *total_index_count)
153 {
154 struct range_info ranges = { .min_index = UINT32_MAX, 0 };
155 unsigned i, start, count;
156 ranges.min_index = UINT32_MAX;
157
158 assert(info->index_size);
159 assert(info->primitive_restart);
160
161 #define SCAN_INDEXES(TYPE) \
162 for (i = 0; i <= draw->count; i++) { \
163 if (i == draw->count || \
164 ((const TYPE *) index_map)[i] == info->restart_index) { \
165 /* cut / restart */ \
166 if (count > 0) { \
167 if (!add_range(info->mode, &ranges, draw->start + start, count, draw->index_bias)) { \
168 return NULL; \
169 } \
170 } \
171 start = i + 1; \
172 count = 0; \
173 } \
174 else { \
175 count++; \
176 } \
177 }
178
179 start = 0;
180 count = 0;
181 switch (info->index_size) {
182 case 1:
183 SCAN_INDEXES(uint8_t);
184 break;
185 case 2:
186 SCAN_INDEXES(uint16_t);
187 break;
188 case 4:
189 SCAN_INDEXES(uint32_t);
190 break;
191 default:
192 assert(!"Bad index size");
193 return NULL;
194 }
195
196 *num_draws = ranges.count;
197 *min_index = ranges.min_index;
198 *max_index = ranges.max_index;
199 *total_index_count = ranges.total_index_count;
200 return ranges.draws;
201 }
202
203 /**
204 * Implement primitive restart by breaking an indexed primitive into
205 * pieces which do not contain restart indexes. Each piece is then
206 * drawn by calling pipe_context::draw_vbo().
207 * \return PIPE_OK if no error, an error code otherwise.
208 */
209 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)210 util_draw_vbo_without_prim_restart(struct pipe_context *context,
211 const struct pipe_draw_info *info,
212 unsigned drawid_offset,
213 const struct pipe_draw_indirect_info *indirect_info,
214 const struct pipe_draw_start_count_bias *draw)
215 {
216 const void *src_map;
217 struct pipe_draw_info new_info = *info;
218 struct pipe_draw_start_count_bias new_draw = *draw;
219 struct pipe_transfer *src_transfer = NULL;
220 DrawElementsIndirectCommand indirect;
221 struct pipe_draw_start_count_bias *direct_draws;
222 unsigned num_draws = 0;
223
224 assert(info->index_size);
225 assert(info->primitive_restart);
226
227 switch (info->index_size) {
228 case 1:
229 case 2:
230 case 4:
231 break;
232 default:
233 assert(!"Bad index size");
234 return PIPE_ERROR_BAD_INPUT;
235 }
236
237 if (indirect_info && indirect_info->buffer) {
238 indirect = read_indirect_elements(context, indirect_info);
239 new_draw.count = indirect.count;
240 new_draw.start = indirect.firstIndex;
241 new_info.instance_count = indirect.primCount;
242 }
243
244 /* Get pointer to the index data */
245 if (!info->has_user_indices) {
246 /* map the index buffer (only the range we need to scan) */
247 src_map = pipe_buffer_map_range(context, info->index.resource,
248 new_draw.start * info->index_size,
249 new_draw.count * info->index_size,
250 PIPE_MAP_READ,
251 &src_transfer);
252 if (!src_map) {
253 return PIPE_ERROR_OUT_OF_MEMORY;
254 }
255 }
256 else {
257 if (!info->index.user) {
258 debug_printf("User-space index buffer is null!");
259 return PIPE_ERROR_BAD_INPUT;
260 }
261 src_map = (const uint8_t *) info->index.user
262 + new_draw.start * info->index_size;
263 }
264
265 unsigned total_index_count;
266 direct_draws = util_prim_restart_convert_to_direct(src_map, &new_info, &new_draw, &num_draws,
267 &new_info.min_index, &new_info.max_index,
268 &total_index_count);
269 /* unmap index buffer */
270 if (src_transfer)
271 pipe_buffer_unmap(context, src_transfer);
272
273 new_info.primitive_restart = false;
274 new_info.index_bounds_valid = true;
275 if (direct_draws)
276 context->draw_vbo(context, &new_info, drawid_offset, NULL, direct_draws, num_draws);
277 free(direct_draws);
278
279 return num_draws > 0 ? PIPE_OK : PIPE_ERROR_OUT_OF_MEMORY;
280 }
281