2 * Copyright 2014 VMware, Inc.
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:
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
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.
28 #include "u_inlines.h"
29 #include "util/u_memory.h"
30 #include "u_prim_restart.h"
37 uint32_t reservedMustBeZero
;
38 } DrawElementsIndirectCommand
;
40 static DrawElementsIndirectCommand
41 read_indirect_elements(struct pipe_context
*context
, struct pipe_draw_indirect_info
*indirect
)
43 DrawElementsIndirectCommand ret
;
44 struct pipe_transfer
*transfer
= 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
,
55 memcpy(&ret
, map
, read_size
);
56 pipe_buffer_unmap(context
, transfer
);
61 util_translate_prim_restart_data(unsigned index_size
,
62 void *src_map
, void *dst_map
,
63 unsigned count
, unsigned restart_index
)
65 if (index_size
== 1) {
66 uint8_t *src
= (uint8_t *) src_map
;
67 uint16_t *dst
= (uint16_t *) dst_map
;
69 for (i
= 0; i
< count
; i
++) {
70 dst
[i
] = (src
[i
] == restart_index
) ? 0xffff : src
[i
];
73 else if (index_size
== 2) {
74 uint16_t *src
= (uint16_t *) src_map
;
75 uint16_t *dst
= (uint16_t *) dst_map
;
77 for (i
= 0; i
< count
; i
++) {
78 dst
[i
] = (src
[i
] == restart_index
) ? 0xffff : src
[i
];
82 uint32_t *src
= (uint32_t *) src_map
;
83 uint32_t *dst
= (uint32_t *) dst_map
;
85 assert(index_size
== 4);
86 for (i
= 0; i
< count
; i
++) {
87 dst
[i
] = (src
[i
] == restart_index
) ? 0xffffffff : src
[i
];
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
97 * Also, index buffers using 1-byte indexes are converted to 2-byte indexes.
100 util_translate_prim_restart_ib(struct pipe_context
*context
,
101 const struct pipe_draw_info
*info
,
102 struct pipe_resource
**dst_buffer
)
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
;
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);
117 if (info
->indirect
) {
118 indirect
= read_indirect_elements(context
, info
->indirect
);
119 count
= indirect
.count
;
120 start
= indirect
.firstIndex
;
123 /* Create new index buffer */
124 *dst_buffer
= pipe_buffer_create(screen
, PIPE_BIND_INDEX_BUFFER
,
126 count
* dst_index_size
);
130 /* Map new / dest index buffer */
131 dst_map
= pipe_buffer_map(context
, *dst_buffer
,
132 PIPE_TRANSFER_WRITE
, &dst_transfer
);
136 if (info
->has_user_indices
)
137 src_map
= (unsigned char*)info
->index
.user
+ start
* src_index_size
;
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
,
148 util_translate_prim_restart_data(src_index_size
, src_map
, dst_map
,
149 info
->count
, info
->restart_index
);
152 pipe_buffer_unmap(context
, src_transfer
);
153 pipe_buffer_unmap(context
, dst_transfer
);
159 pipe_buffer_unmap(context
, src_transfer
);
161 pipe_buffer_unmap(context
, dst_transfer
);
163 pipe_resource_reference(dst_buffer
, NULL
);
164 return PIPE_ERROR_OUT_OF_MEMORY
;
168 /** Helper structs for util_draw_vbo_without_prim_restart() */
171 unsigned start
, count
;
175 struct range
*ranges
;
181 * Helper function for util_draw_vbo_without_prim_restart()
182 * \return true for success, false if out of memory
185 add_range(struct range_info
*info
, unsigned start
, unsigned count
)
187 if (info
->max
== 0) {
189 info
->ranges
= MALLOC(info
->max
* sizeof(struct range
));
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
));
207 info
->ranges
[info
->count
].start
= start
;
208 info
->ranges
[info
->count
].count
= count
;
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.
222 util_draw_vbo_without_prim_restart(struct pipe_context
*context
,
223 const struct pipe_draw_info
*info
)
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
;
235 assert(info
->index_size
);
236 assert(info
->primitive_restart
);
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
;
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
,
254 return PIPE_ERROR_OUT_OF_MEMORY
;
258 if (!info
->index
.user
) {
259 debug_printf("User-space index buffer is null!");
260 return PIPE_ERROR_BAD_INPUT
;
262 src_map
= (const uint8_t *) info
->index
.user
263 + info_start
* info
->index_size
;
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 */ \
272 if (!add_range(&ranges, info_start + start, count)) { \
274 pipe_buffer_unmap(context, src_transfer); \
275 return PIPE_ERROR_OUT_OF_MEMORY; \
288 switch (info
->index_size
) {
290 SCAN_INDEXES(uint8_t);
293 SCAN_INDEXES(uint16_t);
296 SCAN_INDEXES(uint32_t);
299 assert(!"Bad index size");
300 return PIPE_ERROR_BAD_INPUT
;
303 /* unmap index buffer */
305 pipe_buffer_unmap(context
, src_transfer
);
307 /* draw ranges between the restart indexes */
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
);