1/* 2 * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 * 23 * Authors: 24 * Rob Clark <robclark@freedesktop.org> 25 */ 26 27/** 28 * This module converts provides a more convenient front-end to u_indices, 29 * etc, utils to convert primitive types supported not supported by the 30 * hardware. It handles binding new index buffer state, and restoring 31 * previous state after. To use, put something like this at the front of 32 * drivers pipe->draw_vbo(): 33 * 34 * // emulate unsupported primitives: 35 * if (info->mode needs emulating) { 36 * util_primconvert_save_rasterizer_state(ctx->primconvert, ctx->rasterizer); 37 * util_primconvert_draw_vbo(ctx->primconvert, info); 38 * return; 39 * } 40 * 41 */ 42 43#include "pipe/p_state.h" 44#include "util/u_draw.h" 45#include "util/u_inlines.h" 46#include "util/u_memory.h" 47#include "util/u_prim.h" 48#include "util/u_prim_restart.h" 49#include "util/u_upload_mgr.h" 50 51#include "indices/u_indices.h" 52#include "indices/u_primconvert.h" 53 54struct primconvert_context 55{ 56 struct pipe_context *pipe; 57 struct primconvert_config cfg; 58 unsigned api_pv; 59}; 60 61 62struct primconvert_context * 63util_primconvert_create_config(struct pipe_context *pipe, 64 struct primconvert_config *cfg) 65{ 66 struct primconvert_context *pc = CALLOC_STRUCT(primconvert_context); 67 if (!pc) 68 return NULL; 69 pc->pipe = pipe; 70 pc->cfg = *cfg; 71 return pc; 72} 73 74struct primconvert_context * 75util_primconvert_create(struct pipe_context *pipe, uint32_t primtypes_mask) 76{ 77 struct primconvert_config cfg = { .primtypes_mask = primtypes_mask, .restart_primtypes_mask = primtypes_mask }; 78 return util_primconvert_create_config(pipe, &cfg); 79} 80 81void 82util_primconvert_destroy(struct primconvert_context *pc) 83{ 84 FREE(pc); 85} 86 87void 88util_primconvert_save_rasterizer_state(struct primconvert_context *pc, 89 const struct pipe_rasterizer_state 90 *rast) 91{ 92 util_primconvert_save_flatshade_first(pc, rast->flatshade_first); 93} 94 95void 96util_primconvert_save_flatshade_first(struct primconvert_context *pc, bool flatshade_first) 97{ 98 /* if we actually translated the provoking vertex for the buffer, 99 * we would actually need to save/restore rasterizer state. As 100 * it is, we just need to make note of the pv. 101 */ 102 pc->api_pv = flatshade_first ? PV_FIRST : PV_LAST; 103} 104 105static bool 106primconvert_init_draw(struct primconvert_context *pc, 107 const struct pipe_draw_info *info, 108 const struct pipe_draw_start_count_bias *draws, 109 struct pipe_draw_info *new_info, 110 struct pipe_draw_start_count_bias *new_draw) 111{ 112 struct pipe_draw_start_count_bias *direct_draws = NULL; 113 unsigned num_direct_draws = 0; 114 struct pipe_transfer *src_transfer = NULL; 115 u_translate_func trans_func, direct_draw_func; 116 u_generate_func gen_func; 117 const void *src = NULL; 118 void *dst; 119 unsigned ib_offset; 120 unsigned total_index_count = draws->count; 121 void *rewrite_buffer = NULL; 122 123 struct pipe_draw_start_count_bias draw = draws[0]; 124 125 /* Filter out degenerate primitives, u_upload_alloc() will assert 126 * on size==0 so just bail: 127 */ 128 if (!info->primitive_restart && 129 !u_trim_pipe_prim(info->mode, (unsigned*)&draw.count)) 130 return false; 131 132 util_draw_init_info(new_info); 133 new_info->index_bounds_valid = info->index_bounds_valid; 134 new_info->min_index = info->min_index; 135 new_info->max_index = info->max_index; 136 new_info->start_instance = info->start_instance; 137 new_info->instance_count = info->instance_count; 138 new_info->primitive_restart = info->primitive_restart; 139 new_info->restart_index = info->restart_index; 140 if (info->index_size) { 141 enum pipe_prim_type mode = new_info->mode = u_index_prim_type_convert(pc->cfg.primtypes_mask, info->mode, true); 142 unsigned index_size = info->index_size; 143 unsigned offset = draw.start * info->index_size; 144 145 new_info->index_size = u_index_size_convert(info->index_size); 146 147 src = info->has_user_indices ? info->index.user : NULL; 148 if (!src) { 149 /* Map the index range we're interested in (not the whole buffer) */ 150 src = pipe_buffer_map_range(pc->pipe, info->index.resource, 151 offset, 152 draw.count * info->index_size, 153 PIPE_MAP_READ, &src_transfer); 154 offset = 0; 155 draw.start = 0; 156 } 157 const void *restart_src = (const uint8_t *)src + offset; 158 159 /* if the resulting primitive type is not supported by the driver for primitive restart, 160 * or if the original primitive type was not supported by the driver, 161 * the draw needs to be rewritten to not use primitive restart 162 */ 163 if (info->primitive_restart && 164 (!(pc->cfg.restart_primtypes_mask & BITFIELD_BIT(mode)) || 165 !(pc->cfg.primtypes_mask & BITFIELD_BIT(info->mode)))) { 166 /* step 1: rewrite draw to not use primitive primitive restart; 167 * this pre-filters degenerate primitives 168 */ 169 direct_draws = util_prim_restart_convert_to_direct(restart_src, info, &draw, &num_direct_draws, 170 &new_info->min_index, &new_info->max_index, &total_index_count); 171 new_info->primitive_restart = false; 172 /* step 2: get a translator function which does nothing but handle any index size conversions 173 * which may or may not occur (8bit -> 16bit) 174 */ 175 u_index_translator(0xffff, 176 info->mode, index_size, total_index_count, 177 pc->api_pv, pc->api_pv, 178 PR_DISABLE, 179 &mode, &index_size, &new_draw->count, 180 &direct_draw_func); 181 /* this should always be a direct translation */ 182 assert(new_draw->count == total_index_count); 183 /* step 3: allocate a temp buffer for an intermediate rewrite step 184 * if no indices were found, this was a single incomplete restart and can be discarded 185 */ 186 if (total_index_count) 187 rewrite_buffer = malloc(index_size * total_index_count); 188 if (!rewrite_buffer) { 189 if (src_transfer) 190 pipe_buffer_unmap(pc->pipe, src_transfer); 191 return false; 192 } 193 } 194 /* (step 4: get the actual primitive conversion translator function) */ 195 u_index_translator(pc->cfg.primtypes_mask, 196 info->mode, index_size, total_index_count, 197 pc->api_pv, pc->api_pv, 198 new_info->primitive_restart ? PR_ENABLE : PR_DISABLE, 199 &mode, &index_size, &new_draw->count, 200 &trans_func); 201 assert(new_info->mode == mode); 202 assert(new_info->index_size == index_size); 203 } 204 else { 205 enum pipe_prim_type mode = 0; 206 unsigned index_size; 207 208 u_index_generator(pc->cfg.primtypes_mask, 209 info->mode, draw.start, draw.count, 210 pc->api_pv, pc->api_pv, 211 &mode, &index_size, &new_draw->count, 212 &gen_func); 213 new_info->mode = mode; 214 new_info->index_size = index_size; 215 } 216 217 /* (step 5: allocate gpu memory sized for the FINAL index count) */ 218 u_upload_alloc(pc->pipe->stream_uploader, 0, new_info->index_size * new_draw->count, 4, 219 &ib_offset, &new_info->index.resource, &dst); 220 new_draw->start = ib_offset / new_info->index_size; 221 new_draw->index_bias = info->index_size ? draw.index_bias : 0; 222 223 if (info->index_size) { 224 if (num_direct_draws) { 225 uint8_t *ptr = rewrite_buffer; 226 uint8_t *dst_ptr = dst; 227 /* step 6: if rewriting a prim-restart draw to direct draws, 228 * loop over all the direct draws in order to rewrite them into a single index buffer 229 * and draw in order to match the original call 230 */ 231 for (unsigned i = 0; i < num_direct_draws; i++) { 232 /* step 6a: get the index count for this draw, once converted */ 233 unsigned tmp_count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, direct_draws[i].count); 234 /* step 6b: handle index size conversion using the temp buffer; no change in index count 235 * TODO: this step can be optimized out if the index size is known to not change 236 */ 237 direct_draw_func(src, direct_draws[i].start, direct_draws[i].count, direct_draws[i].count, info->restart_index, ptr); 238 /* step 6c: handle the primitive type conversion rewriting to the converted index count */ 239 trans_func(ptr, 0, direct_draws[i].count, tmp_count, info->restart_index, dst_ptr); 240 /* step 6d: increment the temp buffer and mapped final index buffer pointers */ 241 ptr += new_info->index_size * direct_draws[i].count; 242 dst_ptr += new_info->index_size * tmp_count; 243 } 244 /* step 7: set the final index count, which is the converted total index count from the original draw rewrite */ 245 new_draw->count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, total_index_count); 246 } else 247 trans_func(src, draw.start, draw.count, new_draw->count, info->restart_index, dst); 248 249 if (pc->cfg.fixed_prim_restart && new_info->primitive_restart) { 250 new_info->restart_index = (1ull << (new_info->index_size * 8)) - 1; 251 if (info->restart_index != new_info->restart_index) 252 util_translate_prim_restart_data(new_info->index_size, dst, dst, 253 new_draw->count, 254 info->restart_index); 255 } 256 } 257 else { 258 gen_func(draw.start, new_draw->count, dst); 259 } 260 new_info->was_line_loop = info->mode == PIPE_PRIM_LINE_LOOP; 261 262 if (src_transfer) 263 pipe_buffer_unmap(pc->pipe, src_transfer); 264 265 u_upload_unmap(pc->pipe->stream_uploader); 266 267 free(direct_draws); 268 free(rewrite_buffer); 269 return true; 270} 271 272static void 273util_primconvert_draw_single_vbo(struct primconvert_context *pc, 274 const struct pipe_draw_info *info, 275 unsigned drawid_offset, 276 const struct pipe_draw_start_count_bias *draw) 277{ 278 struct pipe_draw_info new_info; 279 struct pipe_draw_start_count_bias new_draw; 280 281 if (!primconvert_init_draw(pc, info, draw, &new_info, &new_draw)) 282 return; 283 /* to the translated draw: */ 284 pc->pipe->draw_vbo(pc->pipe, &new_info, drawid_offset, NULL, &new_draw, 1); 285 286 pipe_resource_reference(&new_info.index.resource, NULL); 287} 288 289void 290util_primconvert_draw_vbo(struct primconvert_context *pc, 291 const struct pipe_draw_info *info, 292 unsigned drawid_offset, 293 const struct pipe_draw_indirect_info *indirect, 294 const struct pipe_draw_start_count_bias *draws, 295 unsigned num_draws) 296{ 297 if (indirect && indirect->buffer) { 298 /* this is stupid, but we're already doing a readback, 299 * so this thing may as well get the rest of the job done 300 */ 301 unsigned draw_count = 0; 302 struct u_indirect_params *new_draws = util_draw_indirect_read(pc->pipe, info, indirect, &draw_count); 303 if (!new_draws) 304 goto cleanup; 305 306 for (unsigned i = 0; i < draw_count; i++) 307 util_primconvert_draw_single_vbo(pc, &new_draws[i].info, drawid_offset + i, &new_draws[i].draw); 308 free(new_draws); 309 } else { 310 unsigned drawid = drawid_offset; 311 for (unsigned i = 0; i < num_draws; i++) { 312 if (draws[i].count && info->instance_count) 313 util_primconvert_draw_single_vbo(pc, info, drawid, &draws[i]); 314 if (info->increment_draw_id) 315 drawid++; 316 } 317 } 318 319cleanup: 320 if (info->take_index_buffer_ownership) { 321 struct pipe_resource *buffer = info->index.resource; 322 pipe_resource_reference(&buffer, NULL); 323 } 324} 325 326void 327util_primconvert_draw_vertex_state(struct primconvert_context *pc, 328 struct pipe_vertex_state *vstate, 329 uint32_t partial_velem_mask, 330 struct pipe_draw_vertex_state_info info, 331 const struct pipe_draw_start_count_bias *draws, 332 unsigned num_draws) 333{ 334 struct pipe_draw_info new_info; 335 struct pipe_draw_start_count_bias new_draw; 336 337 if (pc->cfg.primtypes_mask & BITFIELD_BIT(info.mode)) { 338 pc->pipe->draw_vertex_state(pc->pipe, vstate, partial_velem_mask, info, draws, num_draws); 339 return; 340 } 341 342 if (num_draws > 1) { 343 for (unsigned i = 0; i < num_draws; i++) { 344 if (draws[i].count) 345 util_primconvert_draw_vertex_state(pc, vstate, partial_velem_mask, info, &draws[i], 1); 346 } 347 return; 348 } 349 350 struct pipe_draw_info dinfo = {0}; 351 dinfo.mode = info.mode; 352 dinfo.index_size = 4; 353 dinfo.instance_count = 1; 354 dinfo.index.resource = vstate->input.indexbuf; 355 if (!primconvert_init_draw(pc, &dinfo, draws, &new_info, &new_draw)) 356 return; 357 358 struct pipe_vertex_state *new_state = pc->pipe->screen->create_vertex_state(pc->pipe->screen, 359 &vstate->input.vbuffer, 360 vstate->input.elements, 361 vstate->input.num_elements, 362 new_info.index.resource, 363 vstate->input.full_velem_mask); 364 if (new_state) { 365 struct pipe_draw_vertex_state_info new_vinfo; 366 new_vinfo.mode = new_info.mode; 367 new_vinfo.take_vertex_state_ownership = true; 368 /* to the translated draw: */ 369 pc->pipe->draw_vertex_state(pc->pipe, new_state, partial_velem_mask, new_vinfo, &new_draw, 1); 370 } 371 if (info.take_vertex_state_ownership) 372 pipe_vertex_state_reference(&vstate, NULL); 373 374 pipe_resource_reference(&new_info.index.resource, NULL); 375} 376