u_primconvert.c revision 7ec681f3
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
105void
106util_primconvert_draw_vbo(struct primconvert_context *pc,
107                          const struct pipe_draw_info *info,
108                          unsigned drawid_offset,
109                          const struct pipe_draw_indirect_info *indirect,
110                          const struct pipe_draw_start_count_bias *draws,
111                          unsigned num_draws)
112{
113   struct pipe_draw_info new_info;
114   struct pipe_draw_start_count_bias new_draw;
115   struct pipe_draw_start_count_bias *direct_draws = NULL;
116   unsigned num_direct_draws = 0;
117   struct pipe_transfer *src_transfer = NULL;
118   u_translate_func trans_func, direct_draw_func;
119   u_generate_func gen_func;
120   const void *src = NULL;
121   void *dst;
122   unsigned ib_offset;
123   unsigned total_index_count = draws->count;
124   void *rewrite_buffer = NULL;
125
126   if (indirect && indirect->buffer) {
127      /* this is stupid, but we're already doing a readback,
128       * so this thing may as well get the rest of the job done
129       */
130      unsigned draw_count = 0;
131      struct u_indirect_params *new_draws = util_draw_indirect_read(pc->pipe, info, indirect, &draw_count);
132      if (!new_draws)
133         return;
134
135      for (unsigned i = 0; i < draw_count; i++)
136         util_primconvert_draw_vbo(pc, &new_draws[i].info, drawid_offset + i, NULL, &new_draws[i].draw, 1);
137      free(new_draws);
138      return;
139   }
140
141   if (num_draws > 1) {
142      unsigned drawid = drawid_offset;
143      for (unsigned i = 0; i < num_draws; i++) {
144         if (draws[i].count && info->instance_count)
145            util_primconvert_draw_vbo(pc, info, drawid, NULL, &draws[i], 1);
146         if (info->increment_draw_id)
147            drawid++;
148      }
149      return;
150   }
151
152   const struct pipe_draw_start_count_bias *draw = &draws[0];
153
154   /* Filter out degenerate primitives, u_upload_alloc() will assert
155    * on size==0 so just bail:
156    */
157   if (!info->primitive_restart &&
158       !u_trim_pipe_prim(info->mode, (unsigned*)&draw->count))
159      return;
160
161   util_draw_init_info(&new_info);
162   new_info.index_bounds_valid = info->index_bounds_valid;
163   new_info.min_index = info->min_index;
164   new_info.max_index = info->max_index;
165   new_info.start_instance = info->start_instance;
166   new_info.instance_count = info->instance_count;
167   new_info.primitive_restart = info->primitive_restart;
168   new_info.restart_index = info->restart_index;
169   if (info->index_size) {
170      enum pipe_prim_type mode = new_info.mode = u_index_prim_type_convert(pc->cfg.primtypes_mask, info->mode, true);
171      unsigned index_size = info->index_size;
172      new_info.index_size = u_index_size_convert(info->index_size);
173
174      src = info->has_user_indices ? info->index.user : NULL;
175      if (!src) {
176         src = pipe_buffer_map(pc->pipe, info->index.resource,
177                               PIPE_MAP_READ, &src_transfer);
178      }
179      src = (const uint8_t *)src;
180
181      /* if the resulting primitive type is not supported by the driver for primitive restart,
182       * or if the original primitive type was not supported by the driver,
183       * the draw needs to be rewritten to not use primitive restart
184       */
185      if (info->primitive_restart &&
186          (!(pc->cfg.restart_primtypes_mask & BITFIELD_BIT(mode)) ||
187           !(pc->cfg.primtypes_mask & BITFIELD_BIT(info->mode)))) {
188         /* step 1: rewrite draw to not use primitive primitive restart;
189          *         this pre-filters degenerate primitives
190          */
191         direct_draws = util_prim_restart_convert_to_direct(src, info, draw, &num_direct_draws,
192                                                            &new_info.min_index, &new_info.max_index, &total_index_count);
193         new_info.primitive_restart = false;
194         /* step 2: get a translator function which does nothing but handle any index size conversions
195          * which may or may not occur (8bit -> 16bit)
196          */
197         u_index_translator(0xffff,
198                            info->mode, index_size, total_index_count,
199                            pc->api_pv, pc->api_pv,
200                            PR_DISABLE,
201                            &mode, &index_size, &new_draw.count,
202                            &direct_draw_func);
203         /* this should always be a direct translation */
204         assert(new_draw.count == total_index_count);
205         /* step 3: allocate a temp buffer for an intermediate rewrite step
206          *         if no indices were found, this was a single incomplete restart and can be discarded
207          */
208         if (total_index_count)
209            rewrite_buffer = malloc(index_size * total_index_count);
210         if (!rewrite_buffer) {
211            if (src_transfer)
212               pipe_buffer_unmap(pc->pipe, src_transfer);
213            return;
214         }
215      }
216      /* (step 4: get the actual primitive conversion translator function) */
217      u_index_translator(pc->cfg.primtypes_mask,
218                         info->mode, index_size, total_index_count,
219                         pc->api_pv, pc->api_pv,
220                         new_info.primitive_restart ? PR_ENABLE : PR_DISABLE,
221                         &mode, &index_size, &new_draw.count,
222                         &trans_func);
223      assert(new_info.mode == mode);
224      assert(new_info.index_size == index_size);
225   }
226   else {
227      enum pipe_prim_type mode = 0;
228      unsigned index_size;
229
230      u_index_generator(pc->cfg.primtypes_mask,
231                        info->mode, draw->start, draw->count,
232                        pc->api_pv, pc->api_pv,
233                        &mode, &index_size, &new_draw.count,
234                        &gen_func);
235      new_info.mode = mode;
236      new_info.index_size = index_size;
237   }
238
239   /* (step 5: allocate gpu memory sized for the FINAL index count) */
240   u_upload_alloc(pc->pipe->stream_uploader, 0, new_info.index_size * new_draw.count, 4,
241                  &ib_offset, &new_info.index.resource, &dst);
242   new_draw.start = ib_offset / new_info.index_size;
243   new_draw.index_bias = info->index_size ? draw->index_bias : 0;
244
245   if (info->index_size) {
246      if (num_direct_draws) {
247         uint8_t *ptr = rewrite_buffer;
248         uint8_t *dst_ptr = dst;
249         /* step 6: if rewriting a prim-restart draw to direct draws,
250          * loop over all the direct draws in order to rewrite them into a single index buffer
251          * and draw in order to match the original call
252          */
253         for (unsigned i = 0; i < num_direct_draws; i++) {
254            /* step 6a: get the index count for this draw, once converted */
255            unsigned tmp_count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, direct_draws[i].count);
256            /* step 6b: handle index size conversion using the temp buffer; no change in index count
257             * TODO: this step can be optimized out if the index size is known to not change
258             */
259            direct_draw_func(src, direct_draws[i].start, direct_draws[i].count, direct_draws[i].count, info->restart_index, ptr);
260            /* step 6c: handle the primitive type conversion rewriting to the converted index count */
261            trans_func(ptr, 0, direct_draws[i].count, tmp_count, info->restart_index, dst_ptr);
262            /* step 6d: increment the temp buffer and mapped final index buffer pointers */
263            ptr += new_info.index_size * direct_draws[i].count;
264            dst_ptr += new_info.index_size * tmp_count;
265         }
266         /* step 7: set the final index count, which is the converted total index count from the original draw rewrite */
267         new_draw.count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, total_index_count);
268      } else
269         trans_func(src, draw->start, draw->count, new_draw.count, info->restart_index, dst);
270
271      if (pc->cfg.fixed_prim_restart && new_info.primitive_restart) {
272         new_info.restart_index = (1ull << (new_info.index_size * 8)) - 1;
273         if (info->restart_index != new_info.restart_index)
274            util_translate_prim_restart_data(new_info.index_size, dst, dst,
275                                             new_draw.count,
276                                             info->restart_index);
277      }
278   }
279   else {
280      gen_func(draw->start, new_draw.count, dst);
281   }
282
283   if (src_transfer)
284      pipe_buffer_unmap(pc->pipe, src_transfer);
285
286   u_upload_unmap(pc->pipe->stream_uploader);
287
288   /* to the translated draw: */
289   pc->pipe->draw_vbo(pc->pipe, &new_info, drawid_offset, NULL, &new_draw, 1);
290   free(direct_draws);
291   free(rewrite_buffer);
292
293   pipe_resource_reference(&new_info.index.resource, NULL);
294}
295