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 * Translate an index buffer for primitive restart.
62 * Create a new index buffer which is a copy of the original index buffer
63 * except that instances of 'restart_index' are converted to 0xffff or
65 * Also, index buffers using 1-byte indexes are converted to 2-byte indexes.
68 util_translate_prim_restart_ib(struct pipe_context
*context
,
69 const struct pipe_draw_info
*info
,
70 struct pipe_resource
**dst_buffer
)
72 struct pipe_screen
*screen
= context
->screen
;
73 struct pipe_transfer
*src_transfer
= NULL
, *dst_transfer
= NULL
;
74 void *src_map
= NULL
, *dst_map
= NULL
;
75 const unsigned src_index_size
= info
->index_size
;
76 unsigned dst_index_size
;
77 DrawElementsIndirectCommand indirect
;
78 unsigned count
= info
->count
;
79 unsigned start
= info
->start
;
81 /* 1-byte indexes are converted to 2-byte indexes, 4-byte stays 4-byte */
82 dst_index_size
= MAX2(2, info
->index_size
);
83 assert(dst_index_size
== 2 || dst_index_size
== 4);
86 indirect
= read_indirect_elements(context
, info
->indirect
);
87 count
= indirect
.count
;
88 start
= indirect
.firstIndex
;
91 /* Create new index buffer */
92 *dst_buffer
= pipe_buffer_create(screen
, PIPE_BIND_INDEX_BUFFER
,
94 count
* dst_index_size
);
98 /* Map new / dest index buffer */
99 dst_map
= pipe_buffer_map(context
, *dst_buffer
,
100 PIPE_TRANSFER_WRITE
, &dst_transfer
);
104 if (info
->has_user_indices
)
105 src_map
= (unsigned char*)info
->index
.user
+ start
* src_index_size
;
107 /* Map original / src index buffer */
108 src_map
= pipe_buffer_map_range(context
, info
->index
.resource
,
109 start
* src_index_size
,
110 count
* src_index_size
,
116 if (src_index_size
== 1 && dst_index_size
== 2) {
117 uint8_t *src
= (uint8_t *) src_map
;
118 uint16_t *dst
= (uint16_t *) dst_map
;
120 for (i
= 0; i
< count
; i
++) {
121 dst
[i
] = (src
[i
] == info
->restart_index
) ? 0xffff : src
[i
];
124 else if (src_index_size
== 2 && dst_index_size
== 2) {
125 uint16_t *src
= (uint16_t *) src_map
;
126 uint16_t *dst
= (uint16_t *) dst_map
;
128 for (i
= 0; i
< count
; i
++) {
129 dst
[i
] = (src
[i
] == info
->restart_index
) ? 0xffff : src
[i
];
133 uint32_t *src
= (uint32_t *) src_map
;
134 uint32_t *dst
= (uint32_t *) dst_map
;
136 assert(src_index_size
== 4);
137 assert(dst_index_size
== 4);
138 for (i
= 0; i
< count
; i
++) {
139 dst
[i
] = (src
[i
] == info
->restart_index
) ? 0xffffffff : src
[i
];
144 pipe_buffer_unmap(context
, src_transfer
);
145 pipe_buffer_unmap(context
, dst_transfer
);
151 pipe_buffer_unmap(context
, src_transfer
);
153 pipe_buffer_unmap(context
, dst_transfer
);
155 pipe_resource_reference(dst_buffer
, NULL
);
156 return PIPE_ERROR_OUT_OF_MEMORY
;
160 /** Helper structs for util_draw_vbo_without_prim_restart() */
163 unsigned start
, count
;
167 struct range
*ranges
;
173 * Helper function for util_draw_vbo_without_prim_restart()
174 * \return true for success, false if out of memory
177 add_range(struct range_info
*info
, unsigned start
, unsigned count
)
179 if (info
->max
== 0) {
181 info
->ranges
= MALLOC(info
->max
* sizeof(struct range
));
186 else if (info
->count
== info
->max
) {
187 /* grow the ranges[] array */
188 info
->ranges
= REALLOC(info
->ranges
,
189 info
->max
* sizeof(struct range
),
190 2 * info
->max
* sizeof(struct range
));
199 info
->ranges
[info
->count
].start
= start
;
200 info
->ranges
[info
->count
].count
= count
;
208 * Implement primitive restart by breaking an indexed primitive into
209 * pieces which do not contain restart indexes. Each piece is then
210 * drawn by calling pipe_context::draw_vbo().
211 * \return PIPE_OK if no error, an error code otherwise.
214 util_draw_vbo_without_prim_restart(struct pipe_context
*context
,
215 const struct pipe_draw_info
*info
)
218 struct range_info ranges
= {0};
219 struct pipe_draw_info new_info
;
220 struct pipe_transfer
*src_transfer
= NULL
;
221 unsigned i
, start
, count
;
222 DrawElementsIndirectCommand indirect
;
223 unsigned info_start
= info
->start
;
224 unsigned info_count
= info
->count
;
225 unsigned info_instance_count
= info
->instance_count
;
227 assert(info
->index_size
);
228 assert(info
->primitive_restart
);
230 if (info
->indirect
) {
231 indirect
= read_indirect_elements(context
, info
->indirect
);
232 info_count
= indirect
.count
;
233 info_start
= indirect
.firstIndex
;
234 info_instance_count
= indirect
.primCount
;
237 /* Get pointer to the index data */
238 if (!info
->has_user_indices
) {
239 /* map the index buffer (only the range we need to scan) */
240 src_map
= pipe_buffer_map_range(context
, info
->index
.resource
,
241 info_start
* info
->index_size
,
242 info_count
* info
->index_size
,
246 return PIPE_ERROR_OUT_OF_MEMORY
;
250 if (!info
->index
.user
) {
251 debug_printf("User-space index buffer is null!");
252 return PIPE_ERROR_BAD_INPUT
;
254 src_map
= (const uint8_t *) info
->index
.user
255 + info_start
* info
->index_size
;
258 #define SCAN_INDEXES(TYPE) \
259 for (i = 0; i <= info_count; i++) { \
260 if (i == info_count || \
261 ((const TYPE *) src_map)[i] == info->restart_index) { \
262 /* cut / restart */ \
264 if (!add_range(&ranges, info_start + start, count)) { \
266 pipe_buffer_unmap(context, src_transfer); \
267 return PIPE_ERROR_OUT_OF_MEMORY; \
280 switch (info
->index_size
) {
282 SCAN_INDEXES(uint8_t);
285 SCAN_INDEXES(uint16_t);
288 SCAN_INDEXES(uint32_t);
291 assert(!"Bad index size");
292 return PIPE_ERROR_BAD_INPUT
;
295 /* unmap index buffer */
297 pipe_buffer_unmap(context
, src_transfer
);
299 /* draw ranges between the restart indexes */
301 /* we've effectively remapped this to a direct draw */
302 new_info
.indirect
= NULL
;
303 new_info
.instance_count
= info_instance_count
;
304 new_info
.primitive_restart
= FALSE
;
305 for (i
= 0; i
< ranges
.count
; i
++) {
306 new_info
.start
= ranges
.ranges
[i
].start
;
307 new_info
.count
= ranges
.ranges
[i
].count
;
308 context
->draw_vbo(context
, &new_info
);