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_indirect_info * indirect,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_indirect_info *indirect,
109 const struct pipe_draw_start_count_bias *draws,
110 struct pipe_draw_info *new_info,
111 struct pipe_draw_start_count_bias *new_draw)
112 {
113 struct pipe_draw_start_count_bias *direct_draws = NULL;
114 unsigned num_direct_draws = 0;
115 struct pipe_transfer *src_transfer = NULL;
116 u_translate_func trans_func, direct_draw_func;
117 u_generate_func gen_func;
118 const void *src = NULL;
119 void *dst;
120 unsigned ib_offset;
121 unsigned total_index_count = draws->count;
122 void *rewrite_buffer = NULL;
123
124 struct pipe_draw_start_count_bias draw = draws[0];
125
126 /* Filter out degenerate primitives, u_upload_alloc() will assert
127 * on size==0 so just bail:
128 */
129 if (!info->primitive_restart &&
130 !u_trim_pipe_prim(info->mode, (unsigned*)&draw.count))
131 return false;
132
133 util_draw_init_info(new_info);
134 new_info->index_bounds_valid = info->index_bounds_valid;
135 new_info->min_index = info->min_index;
136 new_info->max_index = info->max_index;
137 new_info->start_instance = info->start_instance;
138 new_info->instance_count = info->instance_count;
139 new_info->primitive_restart = info->primitive_restart;
140 new_info->restart_index = info->restart_index;
141 if (info->index_size) {
142 enum pipe_prim_type mode = new_info->mode = u_index_prim_type_convert(pc->cfg.primtypes_mask, info->mode, true);
143 unsigned index_size = info->index_size;
144 unsigned offset = draw.start * info->index_size;
145
146 new_info->index_size = u_index_size_convert(info->index_size);
147
148 src = info->has_user_indices ? info->index.user : NULL;
149 if (!src) {
150 /* Map the index range we're interested in (not the whole buffer) */
151 src = pipe_buffer_map_range(pc->pipe, info->index.resource,
152 offset,
153 draw.count * info->index_size,
154 PIPE_MAP_READ, &src_transfer);
155 offset = 0;
156 draw.start = 0;
157 }
158 const void *restart_src = (const uint8_t *)src + offset;
159
160 /* if the resulting primitive type is not supported by the driver for primitive restart,
161 * or if the original primitive type was not supported by the driver,
162 * the draw needs to be rewritten to not use primitive restart
163 */
164 if (info->primitive_restart &&
165 (!(pc->cfg.restart_primtypes_mask & BITFIELD_BIT(mode)) ||
166 !(pc->cfg.primtypes_mask & BITFIELD_BIT(info->mode)))) {
167 /* step 1: rewrite draw to not use primitive primitive restart;
168 * this pre-filters degenerate primitives
169 */
170 direct_draws = util_prim_restart_convert_to_direct(restart_src, info, &draw, &num_direct_draws,
171 &new_info->min_index, &new_info->max_index, &total_index_count);
172 new_info->primitive_restart = false;
173 /* step 2: get a translator function which does nothing but handle any index size conversions
174 * which may or may not occur (8bit -> 16bit)
175 */
176 u_index_translator(0xffff,
177 info->mode, index_size, total_index_count,
178 pc->api_pv, pc->api_pv,
179 PR_DISABLE,
180 &mode, &index_size, &new_draw->count,
181 &direct_draw_func);
182 /* this should always be a direct translation */
183 assert(new_draw->count == total_index_count);
184 /* step 3: allocate a temp buffer for an intermediate rewrite step
185 * if no indices were found, this was a single incomplete restart and can be discarded
186 */
187 if (total_index_count)
188 rewrite_buffer = malloc(index_size * total_index_count);
189 if (!rewrite_buffer) {
190 if (src_transfer)
191 pipe_buffer_unmap(pc->pipe, src_transfer);
192 return false;
193 }
194 }
195 /* (step 4: get the actual primitive conversion translator function) */
196 u_index_translator(pc->cfg.primtypes_mask,
197 info->mode, index_size, total_index_count,
198 pc->api_pv, pc->api_pv,
199 new_info->primitive_restart ? PR_ENABLE : PR_DISABLE,
200 &mode, &index_size, &new_draw->count,
201 &trans_func);
202 assert(new_info->mode == mode);
203 assert(new_info->index_size == index_size);
204 }
205 else {
206 enum pipe_prim_type mode = 0;
207 unsigned index_size;
208
209 u_index_generator(pc->cfg.primtypes_mask,
210 info->mode, draw.start, draw.count,
211 pc->api_pv, pc->api_pv,
212 &mode, &index_size, &new_draw->count,
213 &gen_func);
214 new_info->mode = mode;
215 new_info->index_size = index_size;
216 }
217
218 /* (step 5: allocate gpu memory sized for the FINAL index count) */
219 u_upload_alloc(pc->pipe->stream_uploader, 0, new_info->index_size * new_draw->count, 4,
220 &ib_offset, &new_info->index.resource, &dst);
221 new_draw->start = ib_offset / new_info->index_size;
222 new_draw->index_bias = info->index_size ? draw.index_bias : 0;
223
224 if (info->index_size) {
225 if (num_direct_draws) {
226 uint8_t *ptr = rewrite_buffer;
227 uint8_t *dst_ptr = dst;
228 /* step 6: if rewriting a prim-restart draw to direct draws,
229 * loop over all the direct draws in order to rewrite them into a single index buffer
230 * and draw in order to match the original call
231 */
232 for (unsigned i = 0; i < num_direct_draws; i++) {
233 /* step 6a: get the index count for this draw, once converted */
234 unsigned tmp_count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, direct_draws[i].count);
235 /* step 6b: handle index size conversion using the temp buffer; no change in index count
236 * TODO: this step can be optimized out if the index size is known to not change
237 */
238 direct_draw_func(src, direct_draws[i].start, direct_draws[i].count, direct_draws[i].count, info->restart_index, ptr);
239 /* step 6c: handle the primitive type conversion rewriting to the converted index count */
240 trans_func(ptr, 0, direct_draws[i].count, tmp_count, info->restart_index, dst_ptr);
241 /* step 6d: increment the temp buffer and mapped final index buffer pointers */
242 ptr += new_info->index_size * direct_draws[i].count;
243 dst_ptr += new_info->index_size * tmp_count;
244 }
245 /* step 7: set the final index count, which is the converted total index count from the original draw rewrite */
246 new_draw->count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, total_index_count);
247 } else
248 trans_func(src, draw.start, draw.count, new_draw->count, info->restart_index, dst);
249
250 if (pc->cfg.fixed_prim_restart && new_info->primitive_restart) {
251 new_info->restart_index = (1ull << (new_info->index_size * 8)) - 1;
252 if (info->restart_index != new_info->restart_index)
253 util_translate_prim_restart_data(new_info->index_size, dst, dst,
254 new_draw->count,
255 info->restart_index);
256 }
257 }
258 else {
259 gen_func(draw.start, new_draw->count, dst);
260 }
261 new_info->was_line_loop = info->mode == PIPE_PRIM_LINE_LOOP;
262
263 if (src_transfer)
264 pipe_buffer_unmap(pc->pipe, src_transfer);
265
266 u_upload_unmap(pc->pipe->stream_uploader);
267
268 free(direct_draws);
269 free(rewrite_buffer);
270 return true;
271 }
272
273 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)274 util_primconvert_draw_vbo(struct primconvert_context *pc,
275 const struct pipe_draw_info *info,
276 unsigned drawid_offset,
277 const struct pipe_draw_indirect_info *indirect,
278 const struct pipe_draw_start_count_bias *draws,
279 unsigned num_draws)
280 {
281 struct pipe_draw_info new_info;
282 struct pipe_draw_start_count_bias new_draw;
283
284 if (indirect && indirect->buffer) {
285 /* this is stupid, but we're already doing a readback,
286 * so this thing may as well get the rest of the job done
287 */
288 unsigned draw_count = 0;
289 struct u_indirect_params *new_draws = util_draw_indirect_read(pc->pipe, info, indirect, &draw_count);
290 if (!new_draws)
291 return;
292
293 for (unsigned i = 0; i < draw_count; i++)
294 util_primconvert_draw_vbo(pc, &new_draws[i].info, drawid_offset + i, NULL, &new_draws[i].draw, 1);
295 free(new_draws);
296 return;
297 }
298
299 if (num_draws > 1) {
300 unsigned drawid = drawid_offset;
301 for (unsigned i = 0; i < num_draws; i++) {
302 if (draws[i].count && info->instance_count)
303 util_primconvert_draw_vbo(pc, info, drawid, NULL, &draws[i], 1);
304 if (info->increment_draw_id)
305 drawid++;
306 }
307 return;
308 }
309
310 if (!primconvert_init_draw(pc, info, indirect, draws, &new_info, &new_draw))
311 return;
312 /* to the translated draw: */
313 pc->pipe->draw_vbo(pc->pipe, &new_info, drawid_offset, NULL, &new_draw, 1);
314
315 pipe_resource_reference(&new_info.index.resource, NULL);
316 }
317
318 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)319 util_primconvert_draw_vertex_state(struct primconvert_context *pc,
320 struct pipe_vertex_state *vstate,
321 uint32_t partial_velem_mask,
322 struct pipe_draw_vertex_state_info info,
323 const struct pipe_draw_start_count_bias *draws,
324 unsigned num_draws)
325 {
326 struct pipe_draw_info new_info;
327 struct pipe_draw_start_count_bias new_draw;
328
329 if (pc->cfg.primtypes_mask & BITFIELD_BIT(info.mode)) {
330 pc->pipe->draw_vertex_state(pc->pipe, vstate, partial_velem_mask, info, draws, num_draws);
331 return;
332 }
333
334 if (num_draws > 1) {
335 for (unsigned i = 0; i < num_draws; i++) {
336 if (draws[i].count)
337 util_primconvert_draw_vertex_state(pc, vstate, partial_velem_mask, info, &draws[i], 1);
338 }
339 return;
340 }
341
342 struct pipe_draw_info dinfo = {0};
343 dinfo.mode = info.mode;
344 dinfo.index_size = 4;
345 dinfo.instance_count = 1;
346 dinfo.index.resource = vstate->input.indexbuf;
347 if (!primconvert_init_draw(pc, &dinfo, NULL, draws, &new_info, &new_draw))
348 return;
349
350 struct pipe_vertex_state *new_state = pc->pipe->screen->create_vertex_state(pc->pipe->screen,
351 &vstate->input.vbuffer,
352 vstate->input.elements,
353 vstate->input.num_elements,
354 new_info.index.resource,
355 vstate->input.full_velem_mask);
356 if (new_state) {
357 struct pipe_draw_vertex_state_info new_vinfo;
358 new_vinfo.mode = new_info.mode;
359 new_vinfo.take_vertex_state_ownership = true;
360 /* to the translated draw: */
361 pc->pipe->draw_vertex_state(pc->pipe, new_state, partial_velem_mask, new_vinfo, &new_draw, 1);
362 }
363 if (info.take_vertex_state_ownership)
364 pipe_vertex_state_reference(&vstate, NULL);
365
366 pipe_resource_reference(&new_info.index.resource, NULL);
367 }
368