gallium/util: factor out primitive-restart rewriting logic
[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 void
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
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_TRANSFER_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_TRANSFER_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
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
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_TRANSFER_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 }