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