1af69d88dSmrg/*
2af69d88dSmrg * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
3af69d88dSmrg *
4af69d88dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5af69d88dSmrg * copy of this software and associated documentation files (the "Software"),
6af69d88dSmrg * to deal in the Software without restriction, including without limitation
7af69d88dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8af69d88dSmrg * and/or sell copies of the Software, and to permit persons to whom the
9af69d88dSmrg * Software is furnished to do so, subject to the following conditions:
10af69d88dSmrg *
11af69d88dSmrg * The above copyright notice and this permission notice (including the next
12af69d88dSmrg * paragraph) shall be included in all copies or substantial portions of the
13af69d88dSmrg * Software.
14af69d88dSmrg *
15af69d88dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16af69d88dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17af69d88dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19af69d88dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20af69d88dSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21af69d88dSmrg * SOFTWARE.
22af69d88dSmrg *
23af69d88dSmrg * Authors:
24af69d88dSmrg *    Rob Clark <robclark@freedesktop.org>
25af69d88dSmrg */
26af69d88dSmrg
27af69d88dSmrg/**
28af69d88dSmrg * This module converts provides a more convenient front-end to u_indices,
29af69d88dSmrg * etc, utils to convert primitive types supported not supported by the
30af69d88dSmrg * hardware.  It handles binding new index buffer state, and restoring
31af69d88dSmrg * previous state after.  To use, put something like this at the front of
32af69d88dSmrg * drivers pipe->draw_vbo():
33af69d88dSmrg *
34af69d88dSmrg *    // emulate unsupported primitives:
35af69d88dSmrg *    if (info->mode needs emulating) {
36af69d88dSmrg *       util_primconvert_save_rasterizer_state(ctx->primconvert, ctx->rasterizer);
37af69d88dSmrg *       util_primconvert_draw_vbo(ctx->primconvert, info);
38af69d88dSmrg *       return;
39af69d88dSmrg *    }
40af69d88dSmrg *
41af69d88dSmrg */
42af69d88dSmrg
43af69d88dSmrg#include "pipe/p_state.h"
44af69d88dSmrg#include "util/u_draw.h"
45af69d88dSmrg#include "util/u_inlines.h"
46af69d88dSmrg#include "util/u_memory.h"
477ec681f3Smrg#include "util/u_prim.h"
487ec681f3Smrg#include "util/u_prim_restart.h"
4901e04c3fSmrg#include "util/u_upload_mgr.h"
50af69d88dSmrg
51af69d88dSmrg#include "indices/u_indices.h"
52af69d88dSmrg#include "indices/u_primconvert.h"
53af69d88dSmrg
54af69d88dSmrgstruct primconvert_context
55af69d88dSmrg{
56af69d88dSmrg   struct pipe_context *pipe;
577ec681f3Smrg   struct primconvert_config cfg;
58af69d88dSmrg   unsigned api_pv;
59af69d88dSmrg};
60af69d88dSmrg
61af69d88dSmrg
62af69d88dSmrgstruct primconvert_context *
637ec681f3Smrgutil_primconvert_create_config(struct pipe_context *pipe,
647ec681f3Smrg                               struct primconvert_config *cfg)
65af69d88dSmrg{
66af69d88dSmrg   struct primconvert_context *pc = CALLOC_STRUCT(primconvert_context);
67af69d88dSmrg   if (!pc)
68af69d88dSmrg      return NULL;
69af69d88dSmrg   pc->pipe = pipe;
707ec681f3Smrg   pc->cfg = *cfg;
71af69d88dSmrg   return pc;
72af69d88dSmrg}
73af69d88dSmrg
747ec681f3Smrgstruct primconvert_context *
757ec681f3Smrgutil_primconvert_create(struct pipe_context *pipe, uint32_t primtypes_mask)
767ec681f3Smrg{
777ec681f3Smrg   struct primconvert_config cfg = { .primtypes_mask = primtypes_mask, .restart_primtypes_mask = primtypes_mask };
787ec681f3Smrg   return util_primconvert_create_config(pipe, &cfg);
797ec681f3Smrg}
807ec681f3Smrg
81af69d88dSmrgvoid
82af69d88dSmrgutil_primconvert_destroy(struct primconvert_context *pc)
83af69d88dSmrg{
84af69d88dSmrg   FREE(pc);
85af69d88dSmrg}
86af69d88dSmrg
87af69d88dSmrgvoid
88af69d88dSmrgutil_primconvert_save_rasterizer_state(struct primconvert_context *pc,
89af69d88dSmrg                                       const struct pipe_rasterizer_state
90af69d88dSmrg                                       *rast)
917ec681f3Smrg{
927ec681f3Smrg   util_primconvert_save_flatshade_first(pc, rast->flatshade_first);
937ec681f3Smrg}
947ec681f3Smrg
957ec681f3Smrgvoid
967ec681f3Smrgutil_primconvert_save_flatshade_first(struct primconvert_context *pc, bool flatshade_first)
97af69d88dSmrg{
98af69d88dSmrg   /* if we actually translated the provoking vertex for the buffer,
99af69d88dSmrg    * we would actually need to save/restore rasterizer state.  As
100af69d88dSmrg    * it is, we just need to make note of the pv.
101af69d88dSmrg    */
1027ec681f3Smrg   pc->api_pv = flatshade_first ? PV_FIRST : PV_LAST;
103af69d88dSmrg}
104af69d88dSmrg
105af69d88dSmrgvoid
106af69d88dSmrgutil_primconvert_draw_vbo(struct primconvert_context *pc,
1077ec681f3Smrg                          const struct pipe_draw_info *info,
1087ec681f3Smrg                          unsigned drawid_offset,
1097ec681f3Smrg                          const struct pipe_draw_indirect_info *indirect,
1107ec681f3Smrg                          const struct pipe_draw_start_count_bias *draws,
1117ec681f3Smrg                          unsigned num_draws)
112af69d88dSmrg{
113af69d88dSmrg   struct pipe_draw_info new_info;
1147ec681f3Smrg   struct pipe_draw_start_count_bias new_draw;
1157ec681f3Smrg   struct pipe_draw_start_count_bias *direct_draws = NULL;
1167ec681f3Smrg   unsigned num_direct_draws = 0;
11701e04c3fSmrg   struct pipe_transfer *src_transfer = NULL;
1187ec681f3Smrg   u_translate_func trans_func, direct_draw_func;
119af69d88dSmrg   u_generate_func gen_func;
12001e04c3fSmrg   const void *src = NULL;
121af69d88dSmrg   void *dst;
12201e04c3fSmrg   unsigned ib_offset;
1237ec681f3Smrg   unsigned total_index_count = draws->count;
1247ec681f3Smrg   void *rewrite_buffer = NULL;
1257ec681f3Smrg
1267ec681f3Smrg   if (indirect && indirect->buffer) {
1277ec681f3Smrg      /* this is stupid, but we're already doing a readback,
1287ec681f3Smrg       * so this thing may as well get the rest of the job done
1297ec681f3Smrg       */
1307ec681f3Smrg      unsigned draw_count = 0;
1317ec681f3Smrg      struct u_indirect_params *new_draws = util_draw_indirect_read(pc->pipe, info, indirect, &draw_count);
1327ec681f3Smrg      if (!new_draws)
1337ec681f3Smrg         return;
1347ec681f3Smrg
1357ec681f3Smrg      for (unsigned i = 0; i < draw_count; i++)
1367ec681f3Smrg         util_primconvert_draw_vbo(pc, &new_draws[i].info, drawid_offset + i, NULL, &new_draws[i].draw, 1);
1377ec681f3Smrg      free(new_draws);
1387ec681f3Smrg      return;
1397ec681f3Smrg   }
1407ec681f3Smrg
1417ec681f3Smrg   if (num_draws > 1) {
1427ec681f3Smrg      unsigned drawid = drawid_offset;
1437ec681f3Smrg      for (unsigned i = 0; i < num_draws; i++) {
1447ec681f3Smrg         if (draws[i].count && info->instance_count)
1457ec681f3Smrg            util_primconvert_draw_vbo(pc, info, drawid, NULL, &draws[i], 1);
1467ec681f3Smrg         if (info->increment_draw_id)
1477ec681f3Smrg            drawid++;
1487ec681f3Smrg      }
1497ec681f3Smrg      return;
1507ec681f3Smrg   }
1517ec681f3Smrg
1527ec681f3Smrg   const struct pipe_draw_start_count_bias *draw = &draws[0];
1537ec681f3Smrg
1547ec681f3Smrg   /* Filter out degenerate primitives, u_upload_alloc() will assert
1557ec681f3Smrg    * on size==0 so just bail:
1567ec681f3Smrg    */
1577ec681f3Smrg   if (!info->primitive_restart &&
1587ec681f3Smrg       !u_trim_pipe_prim(info->mode, (unsigned*)&draw->count))
1597ec681f3Smrg      return;
160af69d88dSmrg
161af69d88dSmrg   util_draw_init_info(&new_info);
1627ec681f3Smrg   new_info.index_bounds_valid = info->index_bounds_valid;
163af69d88dSmrg   new_info.min_index = info->min_index;
164af69d88dSmrg   new_info.max_index = info->max_index;
16501e04c3fSmrg   new_info.start_instance = info->start_instance;
16601e04c3fSmrg   new_info.instance_count = info->instance_count;
16701e04c3fSmrg   new_info.primitive_restart = info->primitive_restart;
16801e04c3fSmrg   new_info.restart_index = info->restart_index;
16901e04c3fSmrg   if (info->index_size) {
1707ec681f3Smrg      enum pipe_prim_type mode = new_info.mode = u_index_prim_type_convert(pc->cfg.primtypes_mask, info->mode, true);
1717ec681f3Smrg      unsigned index_size = info->index_size;
1727ec681f3Smrg      new_info.index_size = u_index_size_convert(info->index_size);
173af69d88dSmrg
17401e04c3fSmrg      src = info->has_user_indices ? info->index.user : NULL;
175af69d88dSmrg      if (!src) {
17601e04c3fSmrg         src = pipe_buffer_map(pc->pipe, info->index.resource,
1777ec681f3Smrg                               PIPE_MAP_READ, &src_transfer);
178af69d88dSmrg      }
17901e04c3fSmrg      src = (const uint8_t *)src;
1807ec681f3Smrg
1817ec681f3Smrg      /* if the resulting primitive type is not supported by the driver for primitive restart,
1827ec681f3Smrg       * or if the original primitive type was not supported by the driver,
1837ec681f3Smrg       * the draw needs to be rewritten to not use primitive restart
1847ec681f3Smrg       */
1857ec681f3Smrg      if (info->primitive_restart &&
1867ec681f3Smrg          (!(pc->cfg.restart_primtypes_mask & BITFIELD_BIT(mode)) ||
1877ec681f3Smrg           !(pc->cfg.primtypes_mask & BITFIELD_BIT(info->mode)))) {
1887ec681f3Smrg         /* step 1: rewrite draw to not use primitive primitive restart;
1897ec681f3Smrg          *         this pre-filters degenerate primitives
1907ec681f3Smrg          */
1917ec681f3Smrg         direct_draws = util_prim_restart_convert_to_direct(src, info, draw, &num_direct_draws,
1927ec681f3Smrg                                                            &new_info.min_index, &new_info.max_index, &total_index_count);
1937ec681f3Smrg         new_info.primitive_restart = false;
1947ec681f3Smrg         /* step 2: get a translator function which does nothing but handle any index size conversions
1957ec681f3Smrg          * which may or may not occur (8bit -> 16bit)
1967ec681f3Smrg          */
1977ec681f3Smrg         u_index_translator(0xffff,
1987ec681f3Smrg                            info->mode, index_size, total_index_count,
1997ec681f3Smrg                            pc->api_pv, pc->api_pv,
2007ec681f3Smrg                            PR_DISABLE,
2017ec681f3Smrg                            &mode, &index_size, &new_draw.count,
2027ec681f3Smrg                            &direct_draw_func);
2037ec681f3Smrg         /* this should always be a direct translation */
2047ec681f3Smrg         assert(new_draw.count == total_index_count);
2057ec681f3Smrg         /* step 3: allocate a temp buffer for an intermediate rewrite step
2067ec681f3Smrg          *         if no indices were found, this was a single incomplete restart and can be discarded
2077ec681f3Smrg          */
2087ec681f3Smrg         if (total_index_count)
2097ec681f3Smrg            rewrite_buffer = malloc(index_size * total_index_count);
2107ec681f3Smrg         if (!rewrite_buffer) {
2117ec681f3Smrg            if (src_transfer)
2127ec681f3Smrg               pipe_buffer_unmap(pc->pipe, src_transfer);
2137ec681f3Smrg            return;
2147ec681f3Smrg         }
2157ec681f3Smrg      }
2167ec681f3Smrg      /* (step 4: get the actual primitive conversion translator function) */
2177ec681f3Smrg      u_index_translator(pc->cfg.primtypes_mask,
2187ec681f3Smrg                         info->mode, index_size, total_index_count,
2197ec681f3Smrg                         pc->api_pv, pc->api_pv,
2207ec681f3Smrg                         new_info.primitive_restart ? PR_ENABLE : PR_DISABLE,
2217ec681f3Smrg                         &mode, &index_size, &new_draw.count,
2227ec681f3Smrg                         &trans_func);
2237ec681f3Smrg      assert(new_info.mode == mode);
2247ec681f3Smrg      assert(new_info.index_size == index_size);
225af69d88dSmrg   }
226af69d88dSmrg   else {
22701e04c3fSmrg      enum pipe_prim_type mode = 0;
22801e04c3fSmrg      unsigned index_size;
22901e04c3fSmrg
2307ec681f3Smrg      u_index_generator(pc->cfg.primtypes_mask,
2317ec681f3Smrg                        info->mode, draw->start, draw->count,
232af69d88dSmrg                        pc->api_pv, pc->api_pv,
2337ec681f3Smrg                        &mode, &index_size, &new_draw.count,
234af69d88dSmrg                        &gen_func);
23501e04c3fSmrg      new_info.mode = mode;
23601e04c3fSmrg      new_info.index_size = index_size;
237af69d88dSmrg   }
238af69d88dSmrg
2397ec681f3Smrg   /* (step 5: allocate gpu memory sized for the FINAL index count) */
2407ec681f3Smrg   u_upload_alloc(pc->pipe->stream_uploader, 0, new_info.index_size * new_draw.count, 4,
24101e04c3fSmrg                  &ib_offset, &new_info.index.resource, &dst);
2427ec681f3Smrg   new_draw.start = ib_offset / new_info.index_size;
2437ec681f3Smrg   new_draw.index_bias = info->index_size ? draw->index_bias : 0;
244af69d88dSmrg
24501e04c3fSmrg   if (info->index_size) {
2467ec681f3Smrg      if (num_direct_draws) {
2477ec681f3Smrg         uint8_t *ptr = rewrite_buffer;
2487ec681f3Smrg         uint8_t *dst_ptr = dst;
2497ec681f3Smrg         /* step 6: if rewriting a prim-restart draw to direct draws,
2507ec681f3Smrg          * loop over all the direct draws in order to rewrite them into a single index buffer
2517ec681f3Smrg          * and draw in order to match the original call
2527ec681f3Smrg          */
2537ec681f3Smrg         for (unsigned i = 0; i < num_direct_draws; i++) {
2547ec681f3Smrg            /* step 6a: get the index count for this draw, once converted */
2557ec681f3Smrg            unsigned tmp_count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, direct_draws[i].count);
2567ec681f3Smrg            /* step 6b: handle index size conversion using the temp buffer; no change in index count
2577ec681f3Smrg             * TODO: this step can be optimized out if the index size is known to not change
2587ec681f3Smrg             */
2597ec681f3Smrg            direct_draw_func(src, direct_draws[i].start, direct_draws[i].count, direct_draws[i].count, info->restart_index, ptr);
2607ec681f3Smrg            /* step 6c: handle the primitive type conversion rewriting to the converted index count */
2617ec681f3Smrg            trans_func(ptr, 0, direct_draws[i].count, tmp_count, info->restart_index, dst_ptr);
2627ec681f3Smrg            /* step 6d: increment the temp buffer and mapped final index buffer pointers */
2637ec681f3Smrg            ptr += new_info.index_size * direct_draws[i].count;
2647ec681f3Smrg            dst_ptr += new_info.index_size * tmp_count;
2657ec681f3Smrg         }
2667ec681f3Smrg         /* step 7: set the final index count, which is the converted total index count from the original draw rewrite */
2677ec681f3Smrg         new_draw.count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, total_index_count);
2687ec681f3Smrg      } else
2697ec681f3Smrg         trans_func(src, draw->start, draw->count, new_draw.count, info->restart_index, dst);
2707ec681f3Smrg
2717ec681f3Smrg      if (pc->cfg.fixed_prim_restart && new_info.primitive_restart) {
2727ec681f3Smrg         new_info.restart_index = (1ull << (new_info.index_size * 8)) - 1;
2737ec681f3Smrg         if (info->restart_index != new_info.restart_index)
2747ec681f3Smrg            util_translate_prim_restart_data(new_info.index_size, dst, dst,
2757ec681f3Smrg                                             new_draw.count,
2767ec681f3Smrg                                             info->restart_index);
2777ec681f3Smrg      }
278af69d88dSmrg   }
279af69d88dSmrg   else {
2807ec681f3Smrg      gen_func(draw->start, new_draw.count, dst);
281af69d88dSmrg   }
282af69d88dSmrg
283af69d88dSmrg   if (src_transfer)
284af69d88dSmrg      pipe_buffer_unmap(pc->pipe, src_transfer);
285af69d88dSmrg
28601e04c3fSmrg   u_upload_unmap(pc->pipe->stream_uploader);
287af69d88dSmrg
288af69d88dSmrg   /* to the translated draw: */
2897ec681f3Smrg   pc->pipe->draw_vbo(pc->pipe, &new_info, drawid_offset, NULL, &new_draw, 1);
2907ec681f3Smrg   free(direct_draws);
2917ec681f3Smrg   free(rewrite_buffer);
292af69d88dSmrg
29301e04c3fSmrg   pipe_resource_reference(&new_info.index.resource, NULL);
294af69d88dSmrg}
295