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