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