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