• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
54 struct primconvert_context
55 {
56    struct pipe_context *pipe;
57    struct primconvert_config cfg;
58    unsigned api_pv;
59 };
60 
61 
62 struct primconvert_context *
util_primconvert_create_config(struct pipe_context * pipe,struct primconvert_config * cfg)63 util_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 
74 struct primconvert_context *
util_primconvert_create(struct pipe_context * pipe,uint32_t primtypes_mask)75 util_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 
81 void
util_primconvert_destroy(struct primconvert_context * pc)82 util_primconvert_destroy(struct primconvert_context *pc)
83 {
84    FREE(pc);
85 }
86 
87 void
util_primconvert_save_rasterizer_state(struct primconvert_context * pc,const struct pipe_rasterizer_state * rast)88 util_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 
95 void
util_primconvert_save_flatshade_first(struct primconvert_context * pc,bool flatshade_first)96 util_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 
105 static bool
primconvert_init_draw(struct primconvert_context * pc,const struct pipe_draw_info * info,const struct pipe_draw_start_count_bias * draws,struct pipe_draw_info * new_info,struct pipe_draw_start_count_bias * new_draw)106 primconvert_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 
134    /* Because we've changed the index buffer, the original min_index/max_index
135     * for the draw are no longer valid. That's ok, but we need to tell drivers
136     * so they don't optimize incorrectly.
137     */
138    new_info->index_bounds_valid = false;
139    new_info->min_index = 0;
140    new_info->max_index = ~0;
141 
142    new_info->start_instance = info->start_instance;
143    new_info->instance_count = info->instance_count;
144    new_info->primitive_restart = info->primitive_restart;
145    new_info->restart_index = info->restart_index;
146    if (info->index_size) {
147       enum mesa_prim mode = new_info->mode = u_index_prim_type_convert(pc->cfg.primtypes_mask, info->mode, true);
148       unsigned index_size = info->index_size;
149       unsigned offset = draw.start * info->index_size;
150 
151       new_info->index_size = u_index_size_convert(info->index_size);
152 
153       src = info->has_user_indices ? info->index.user : NULL;
154       if (!src) {
155          /* Map the index range we're interested in (not the whole buffer) */
156          src = pipe_buffer_map_range(pc->pipe, info->index.resource,
157                                      offset,
158                                      draw.count * info->index_size,
159                                      PIPE_MAP_READ, &src_transfer);
160          offset = 0;
161          draw.start = 0;
162       }
163       const void *restart_src = (const uint8_t *)src  + offset;
164 
165       /* if the resulting primitive type is not supported by the driver for primitive restart,
166        * or if the original primitive type was not supported by the driver,
167        * the draw needs to be rewritten to not use primitive restart
168        */
169       if (info->primitive_restart &&
170           (!(pc->cfg.restart_primtypes_mask & BITFIELD_BIT(mode)) ||
171            !(pc->cfg.primtypes_mask & BITFIELD_BIT(info->mode)))) {
172          /* step 1: rewrite draw to not use primitive primitive restart;
173           *         this pre-filters degenerate primitives
174           */
175          direct_draws = util_prim_restart_convert_to_direct(restart_src, info, &draw, &num_direct_draws,
176                                                             &new_info->min_index, &new_info->max_index, &total_index_count);
177          new_info->primitive_restart = false;
178          /* step 2: get a translator function which does nothing but handle any index size conversions
179           * which may or may not occur (8bit -> 16bit)
180           */
181          u_index_translator(0xffff,
182                             info->mode, index_size, total_index_count,
183                             pc->api_pv, pc->api_pv,
184                             PR_DISABLE,
185                             &mode, &index_size, &new_draw->count,
186                             &direct_draw_func);
187          /* this should always be a direct translation */
188          assert(new_draw->count == total_index_count);
189          /* step 3: allocate a temp buffer for an intermediate rewrite step
190           *         if no indices were found, this was a single incomplete restart and can be discarded
191           */
192          if (total_index_count)
193             rewrite_buffer = malloc(index_size * total_index_count);
194          if (!rewrite_buffer) {
195             if (src_transfer)
196                pipe_buffer_unmap(pc->pipe, src_transfer);
197             return false;
198          }
199       }
200       /* (step 4: get the actual primitive conversion translator function) */
201       u_index_translator(pc->cfg.primtypes_mask,
202                          info->mode, index_size, total_index_count,
203                          pc->api_pv, pc->api_pv,
204                          new_info->primitive_restart ? PR_ENABLE : PR_DISABLE,
205                          &mode, &index_size, &new_draw->count,
206                          &trans_func);
207       assert(new_info->mode == mode);
208       assert(new_info->index_size == index_size);
209    }
210    else {
211       enum mesa_prim mode = 0;
212       unsigned index_size;
213 
214       u_index_generator(pc->cfg.primtypes_mask,
215                         info->mode, draw.start, draw.count,
216                         pc->api_pv, pc->api_pv,
217                         &mode, &index_size, &new_draw->count,
218                         &gen_func);
219       new_info->mode = mode;
220       new_info->index_size = index_size;
221    }
222 
223    /* (step 5: allocate gpu memory sized for the FINAL index count) */
224    uint64_t new_size = (uint64_t)new_info->index_size * new_draw->count;
225    if (new_size > UINT_MAX)
226       return false;
227    u_upload_alloc(pc->pipe->stream_uploader, 0, new_size, 4,
228                   &ib_offset, &new_info->index.resource, &dst);
229    if (!dst)
230       return false;
231    new_draw->start = ib_offset / new_info->index_size;
232    new_draw->index_bias = info->index_size ? draw.index_bias : 0;
233 
234    if (info->index_size) {
235       if (num_direct_draws) {
236          uint8_t *ptr = rewrite_buffer;
237          uint8_t *dst_ptr = dst;
238          /* step 6: if rewriting a prim-restart draw to direct draws,
239           * loop over all the direct draws in order to rewrite them into a single index buffer
240           * and draw in order to match the original call
241           */
242          for (unsigned i = 0; i < num_direct_draws; i++) {
243             /* step 6a: get the index count for this draw, once converted */
244             unsigned tmp_count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, direct_draws[i].count);
245             /* step 6b: handle index size conversion using the temp buffer; no change in index count
246              * TODO: this step can be optimized out if the index size is known to not change
247              */
248             direct_draw_func(src, direct_draws[i].start, direct_draws[i].count, direct_draws[i].count, info->restart_index, ptr);
249             /* step 6c: handle the primitive type conversion rewriting to the converted index count */
250             trans_func(ptr, 0, direct_draws[i].count, tmp_count, info->restart_index, dst_ptr);
251             /* step 6d: increment the temp buffer and mapped final index buffer pointers */
252             ptr += new_info->index_size * direct_draws[i].count;
253             dst_ptr += new_info->index_size * tmp_count;
254          }
255          /* step 7: set the final index count, which is the converted total index count from the original draw rewrite */
256          new_draw->count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, total_index_count);
257       } else
258          trans_func(src, draw.start, draw.count, new_draw->count, info->restart_index, dst);
259 
260       if (pc->cfg.fixed_prim_restart && new_info->primitive_restart) {
261          new_info->restart_index = (1ull << (new_info->index_size * 8)) - 1;
262          if (info->restart_index != new_info->restart_index)
263             util_translate_prim_restart_data(new_info->index_size, dst, dst,
264                                              new_draw->count,
265                                              info->restart_index);
266       }
267    }
268    else {
269       gen_func(draw.start, new_draw->count, dst);
270    }
271    new_info->was_line_loop = info->mode == MESA_PRIM_LINE_LOOP;
272 
273    if (src_transfer)
274       pipe_buffer_unmap(pc->pipe, src_transfer);
275 
276    u_upload_unmap(pc->pipe->stream_uploader);
277 
278    free(direct_draws);
279    free(rewrite_buffer);
280    return true;
281 }
282 
283 static void
util_primconvert_draw_single_vbo(struct primconvert_context * pc,const struct pipe_draw_info * info,unsigned drawid_offset,const struct pipe_draw_start_count_bias * draw)284 util_primconvert_draw_single_vbo(struct primconvert_context *pc,
285                                  const struct pipe_draw_info *info,
286                                  unsigned drawid_offset,
287                                  const struct pipe_draw_start_count_bias *draw)
288 {
289    struct pipe_draw_info new_info;
290    struct pipe_draw_start_count_bias new_draw;
291 
292    if (!primconvert_init_draw(pc, info, draw, &new_info, &new_draw))
293       return;
294    /* to the translated draw: */
295    pc->pipe->draw_vbo(pc->pipe, &new_info, drawid_offset, NULL, &new_draw, 1);
296 
297    pipe_resource_reference(&new_info.index.resource, NULL);
298 }
299 
300 void
util_primconvert_draw_vbo(struct primconvert_context * pc,const struct pipe_draw_info * info,unsigned drawid_offset,const struct pipe_draw_indirect_info * indirect,const struct pipe_draw_start_count_bias * draws,unsigned num_draws)301 util_primconvert_draw_vbo(struct primconvert_context *pc,
302                           const struct pipe_draw_info *info,
303                           unsigned drawid_offset,
304                           const struct pipe_draw_indirect_info *indirect,
305                           const struct pipe_draw_start_count_bias *draws,
306                           unsigned num_draws)
307 {
308    if (indirect && indirect->buffer) {
309       /* this is stupid, but we're already doing a readback,
310        * so this thing may as well get the rest of the job done
311        */
312       unsigned draw_count = 0;
313       struct u_indirect_params *new_draws = util_draw_indirect_read(pc->pipe, info, indirect, &draw_count);
314       if (!new_draws)
315          goto cleanup;
316 
317       for (unsigned i = 0; i < draw_count; i++)
318          util_primconvert_draw_single_vbo(pc, &new_draws[i].info, drawid_offset + i, &new_draws[i].draw);
319       free(new_draws);
320    } else {
321       unsigned drawid = drawid_offset;
322       for (unsigned i = 0; i < num_draws; i++) {
323          if (draws[i].count && info->instance_count)
324             util_primconvert_draw_single_vbo(pc, info, drawid, &draws[i]);
325          if (info->increment_draw_id)
326             drawid++;
327       }
328    }
329 
330 cleanup:
331    if (info->take_index_buffer_ownership) {
332       struct pipe_resource *buffer = info->index.resource;
333       pipe_resource_reference(&buffer, NULL);
334    }
335 }
336 
337 void
util_primconvert_draw_vertex_state(struct primconvert_context * pc,struct pipe_vertex_state * vstate,uint32_t partial_velem_mask,struct pipe_draw_vertex_state_info info,const struct pipe_draw_start_count_bias * draws,unsigned num_draws)338 util_primconvert_draw_vertex_state(struct primconvert_context *pc,
339                                    struct pipe_vertex_state *vstate,
340                                    uint32_t partial_velem_mask,
341                                    struct pipe_draw_vertex_state_info info,
342                                    const struct pipe_draw_start_count_bias *draws,
343                                    unsigned num_draws)
344 {
345    struct pipe_draw_info new_info;
346    struct pipe_draw_start_count_bias new_draw;
347 
348    if (pc->cfg.primtypes_mask & BITFIELD_BIT(info.mode)) {
349       pc->pipe->draw_vertex_state(pc->pipe, vstate, partial_velem_mask, info, draws, num_draws);
350       return;
351    }
352 
353    if (num_draws > 1) {
354       for (unsigned i = 0; i < num_draws; i++) {
355          if (draws[i].count)
356             util_primconvert_draw_vertex_state(pc, vstate, partial_velem_mask, info, &draws[i], 1);
357       }
358       return;
359    }
360 
361    struct pipe_draw_info dinfo = {0};
362    dinfo.mode = info.mode;
363    dinfo.index_size = 4;
364    dinfo.instance_count = 1;
365    dinfo.index.resource = vstate->input.indexbuf;
366    if (!primconvert_init_draw(pc, &dinfo, draws, &new_info, &new_draw))
367       return;
368 
369    struct pipe_vertex_state *new_state = pc->pipe->screen->create_vertex_state(pc->pipe->screen,
370                                                                                &vstate->input.vbuffer,
371                                                                                vstate->input.elements,
372                                                                                vstate->input.num_elements,
373                                                                                new_info.index.resource,
374                                                                                vstate->input.full_velem_mask);
375    if (new_state) {
376       struct pipe_draw_vertex_state_info new_vinfo;
377       new_vinfo.mode = new_info.mode;
378       new_vinfo.take_vertex_state_ownership = true;
379       /* to the translated draw: */
380       pc->pipe->draw_vertex_state(pc->pipe, new_state, partial_velem_mask, new_vinfo, &new_draw, 1);
381    }
382    if (info.take_vertex_state_ownership)
383       pipe_vertex_state_reference(&vstate, NULL);
384 
385    pipe_resource_reference(&new_info.index.resource, NULL);
386 }
387