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#include "u_prim.h" 32 33typedef struct { 34 uint32_t count; 35 uint32_t primCount; 36 uint32_t firstIndex; 37 int32_t baseVertex; 38 uint32_t reservedMustBeZero; 39} DrawElementsIndirectCommand; 40 41static DrawElementsIndirectCommand 42read_indirect_elements(struct pipe_context *context, const struct pipe_draw_indirect_info *indirect) 43{ 44 DrawElementsIndirectCommand ret; 45 struct pipe_transfer *transfer = NULL; 46 void *map = NULL; 47 /* we only need the first 3 members */ 48 unsigned read_size = 3 * sizeof(uint32_t); 49 assert(indirect->buffer->width0 > 3 * sizeof(uint32_t)); 50 map = pipe_buffer_map_range(context, indirect->buffer, 51 indirect->offset, 52 read_size, 53 PIPE_MAP_READ, 54 &transfer); 55 assert(map); 56 memcpy(&ret, map, read_size); 57 pipe_buffer_unmap(context, transfer); 58 return ret; 59} 60 61void 62util_translate_prim_restart_data(unsigned index_size, 63 void *src_map, void *dst_map, 64 unsigned count, unsigned restart_index) 65{ 66 if (index_size == 1) { 67 uint8_t *src = (uint8_t *) src_map; 68 uint16_t *dst = (uint16_t *) dst_map; 69 unsigned i; 70 for (i = 0; i < count; i++) { 71 dst[i] = (src[i] == restart_index) ? 0xffff : src[i]; 72 } 73 } 74 else if (index_size == 2) { 75 uint16_t *src = (uint16_t *) src_map; 76 uint16_t *dst = (uint16_t *) dst_map; 77 unsigned i; 78 for (i = 0; i < count; i++) { 79 dst[i] = (src[i] == restart_index) ? 0xffff : src[i]; 80 } 81 } 82 else { 83 uint32_t *src = (uint32_t *) src_map; 84 uint32_t *dst = (uint32_t *) dst_map; 85 unsigned i; 86 assert(index_size == 4); 87 for (i = 0; i < count; i++) { 88 dst[i] = (src[i] == restart_index) ? 0xffffffff : src[i]; 89 } 90 } 91} 92 93/** 94 * Translate an index buffer for primitive restart. 95 * Create a new index buffer which is a copy of the original index buffer 96 * except that instances of 'restart_index' are converted to 0xffff or 97 * 0xffffffff. 98 * Also, index buffers using 1-byte indexes are converted to 2-byte indexes. 99 */ 100enum pipe_error 101util_translate_prim_restart_ib(struct pipe_context *context, 102 const struct pipe_draw_info *info, 103 const struct pipe_draw_indirect_info *indirect_info, 104 const struct pipe_draw_start_count_bias *draw, 105 struct pipe_resource **dst_buffer) 106{ 107 struct pipe_screen *screen = context->screen; 108 struct pipe_transfer *src_transfer = NULL, *dst_transfer = NULL; 109 void *src_map = NULL, *dst_map = NULL; 110 const unsigned src_index_size = info->index_size; 111 unsigned dst_index_size; 112 DrawElementsIndirectCommand indirect; 113 unsigned count = draw->count; 114 unsigned start = draw->start; 115 116 /* 1-byte indexes are converted to 2-byte indexes, 4-byte stays 4-byte */ 117 dst_index_size = MAX2(2, info->index_size); 118 assert(dst_index_size == 2 || dst_index_size == 4); 119 120 if (indirect_info && indirect_info->buffer) { 121 indirect = read_indirect_elements(context, indirect_info); 122 count = indirect.count; 123 start = indirect.firstIndex; 124 } 125 126 /* Create new index buffer */ 127 *dst_buffer = pipe_buffer_create(screen, PIPE_BIND_INDEX_BUFFER, 128 PIPE_USAGE_STREAM, 129 count * dst_index_size); 130 if (!*dst_buffer) 131 goto error; 132 133 /* Map new / dest index buffer */ 134 dst_map = pipe_buffer_map(context, *dst_buffer, 135 PIPE_MAP_WRITE, &dst_transfer); 136 if (!dst_map) 137 goto error; 138 139 if (info->has_user_indices) 140 src_map = (unsigned char*)info->index.user + start * src_index_size; 141 else 142 /* Map original / src index buffer */ 143 src_map = pipe_buffer_map_range(context, info->index.resource, 144 start * src_index_size, 145 count * src_index_size, 146 PIPE_MAP_READ, 147 &src_transfer); 148 if (!src_map) 149 goto error; 150 151 util_translate_prim_restart_data(src_index_size, src_map, dst_map, 152 count, info->restart_index); 153 154 if (src_transfer) 155 pipe_buffer_unmap(context, src_transfer); 156 pipe_buffer_unmap(context, dst_transfer); 157 158 return PIPE_OK; 159 160error: 161 if (src_transfer) 162 pipe_buffer_unmap(context, src_transfer); 163 if (dst_transfer) 164 pipe_buffer_unmap(context, dst_transfer); 165 if (*dst_buffer) 166 pipe_resource_reference(dst_buffer, NULL); 167 return PIPE_ERROR_OUT_OF_MEMORY; 168} 169 170 171/** Helper structs for util_draw_vbo_without_prim_restart() */ 172 173struct range_info { 174 struct pipe_draw_start_count_bias *draws; 175 unsigned count, max; 176 unsigned min_index, max_index; 177 unsigned total_index_count; 178}; 179 180 181/** 182 * Helper function for util_draw_vbo_without_prim_restart() 183 * \return true for success, false if out of memory 184 */ 185static boolean 186add_range(enum pipe_prim_type mode, struct range_info *info, unsigned start, unsigned count, unsigned index_bias) 187{ 188 /* degenerate primitive: ignore */ 189 if (!u_trim_pipe_prim(mode, (unsigned*)&count)) 190 return TRUE; 191 192 if (info->max == 0) { 193 info->max = 10; 194 info->draws = MALLOC(info->max * sizeof(struct pipe_draw_start_count_bias)); 195 if (!info->draws) { 196 return FALSE; 197 } 198 } 199 else if (info->count == info->max) { 200 /* grow the draws[] array */ 201 info->draws = REALLOC(info->draws, 202 info->max * sizeof(struct pipe_draw_start_count_bias), 203 2 * info->max * sizeof(struct pipe_draw_start_count_bias)); 204 if (!info->draws) { 205 return FALSE; 206 } 207 208 info->max *= 2; 209 } 210 info->min_index = MIN2(info->min_index, start); 211 info->max_index = MAX2(info->max_index, start + count - 1); 212 213 /* save the range */ 214 info->draws[info->count].start = start; 215 info->draws[info->count].count = count; 216 info->draws[info->count].index_bias = index_bias; 217 info->count++; 218 info->total_index_count += count; 219 220 return TRUE; 221} 222 223struct pipe_draw_start_count_bias * 224util_prim_restart_convert_to_direct(const void *index_map, 225 const struct pipe_draw_info *info, 226 const struct pipe_draw_start_count_bias *draw, 227 unsigned *num_draws, 228 unsigned *min_index, 229 unsigned *max_index, 230 unsigned *total_index_count) 231{ 232 struct range_info ranges = { .min_index = UINT32_MAX, 0 }; 233 unsigned i, start, count; 234 ranges.min_index = UINT32_MAX; 235 236 assert(info->index_size); 237 assert(info->primitive_restart); 238 239#define SCAN_INDEXES(TYPE) \ 240 for (i = 0; i <= draw->count; i++) { \ 241 if (i == draw->count || \ 242 ((const TYPE *) index_map)[i] == info->restart_index) { \ 243 /* cut / restart */ \ 244 if (count > 0) { \ 245 if (!add_range(info->mode, &ranges, draw->start + start, count, draw->index_bias)) { \ 246 return NULL; \ 247 } \ 248 } \ 249 start = i + 1; \ 250 count = 0; \ 251 } \ 252 else { \ 253 count++; \ 254 } \ 255 } 256 257 start = 0; 258 count = 0; 259 switch (info->index_size) { 260 case 1: 261 SCAN_INDEXES(uint8_t); 262 break; 263 case 2: 264 SCAN_INDEXES(uint16_t); 265 break; 266 case 4: 267 SCAN_INDEXES(uint32_t); 268 break; 269 default: 270 assert(!"Bad index size"); 271 return NULL; 272 } 273 274 *num_draws = ranges.count; 275 *min_index = ranges.min_index; 276 *max_index = ranges.max_index; 277 *total_index_count = ranges.total_index_count; 278 return ranges.draws; 279} 280 281/** 282 * Implement primitive restart by breaking an indexed primitive into 283 * pieces which do not contain restart indexes. Each piece is then 284 * drawn by calling pipe_context::draw_vbo(). 285 * \return PIPE_OK if no error, an error code otherwise. 286 */ 287enum pipe_error 288util_draw_vbo_without_prim_restart(struct pipe_context *context, 289 const struct pipe_draw_info *info, 290 unsigned drawid_offset, 291 const struct pipe_draw_indirect_info *indirect_info, 292 const struct pipe_draw_start_count_bias *draw) 293{ 294 const void *src_map; 295 struct pipe_draw_info new_info = *info; 296 struct pipe_draw_start_count_bias new_draw = *draw; 297 struct pipe_transfer *src_transfer = NULL; 298 DrawElementsIndirectCommand indirect; 299 struct pipe_draw_start_count_bias *direct_draws; 300 unsigned num_draws = 0; 301 302 assert(info->index_size); 303 assert(info->primitive_restart); 304 305 switch (info->index_size) { 306 case 1: 307 case 2: 308 case 4: 309 break; 310 default: 311 assert(!"Bad index size"); 312 return PIPE_ERROR_BAD_INPUT; 313 } 314 315 if (indirect_info && indirect_info->buffer) { 316 indirect = read_indirect_elements(context, indirect_info); 317 new_draw.count = indirect.count; 318 new_draw.start = indirect.firstIndex; 319 new_info.instance_count = indirect.primCount; 320 } 321 322 /* Get pointer to the index data */ 323 if (!info->has_user_indices) { 324 /* map the index buffer (only the range we need to scan) */ 325 src_map = pipe_buffer_map_range(context, info->index.resource, 326 new_draw.start * info->index_size, 327 new_draw.count * info->index_size, 328 PIPE_MAP_READ, 329 &src_transfer); 330 if (!src_map) { 331 return PIPE_ERROR_OUT_OF_MEMORY; 332 } 333 } 334 else { 335 if (!info->index.user) { 336 debug_printf("User-space index buffer is null!"); 337 return PIPE_ERROR_BAD_INPUT; 338 } 339 src_map = (const uint8_t *) info->index.user 340 + new_draw.start * info->index_size; 341 } 342 343 unsigned total_index_count; 344 direct_draws = util_prim_restart_convert_to_direct(src_map, &new_info, &new_draw, &num_draws, 345 &new_info.min_index, &new_info.max_index, 346 &total_index_count); 347 /* unmap index buffer */ 348 if (src_transfer) 349 pipe_buffer_unmap(context, src_transfer); 350 351 new_info.primitive_restart = FALSE; 352 new_info.index_bounds_valid = true; 353 if (direct_draws) 354 context->draw_vbo(context, &new_info, drawid_offset, NULL, direct_draws, num_draws); 355 free(direct_draws); 356 357 return num_draws > 0 ? PIPE_OK : PIPE_ERROR_OUT_OF_MEMORY; 358} 359