1/*
2 * Copyright 2014 VMware, Inc.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26
27
28#include "u_inlines.h"
29#include "util/u_memory.h"
30#include "u_prim_restart.h"
31#include "u_prim.h"
32
33typedef struct {
34  uint32_t count;
35  uint32_t primCount;
36  uint32_t firstIndex;
37  int32_t  baseVertex;
38  uint32_t reservedMustBeZero;
39} DrawElementsIndirectCommand;
40
41static DrawElementsIndirectCommand
42read_indirect_elements(struct pipe_context *context, const struct pipe_draw_indirect_info *indirect)
43{
44   DrawElementsIndirectCommand ret;
45   struct pipe_transfer *transfer = NULL;
46   void *map = NULL;
47   /* we only need the first 3 members */
48   unsigned read_size = 3 * sizeof(uint32_t);
49   assert(indirect->buffer->width0 > 3 * sizeof(uint32_t));
50   map = pipe_buffer_map_range(context, indirect->buffer,
51                                   indirect->offset,
52                                   read_size,
53                                   PIPE_MAP_READ,
54                                   &transfer);
55   assert(map);
56   memcpy(&ret, map, read_size);
57   pipe_buffer_unmap(context, transfer);
58   return ret;
59}
60
61void
62util_translate_prim_restart_data(unsigned index_size,
63                                 void *src_map, void *dst_map,
64                                 unsigned count, unsigned restart_index)
65{
66   if (index_size == 1) {
67      uint8_t *src = (uint8_t *) src_map;
68      uint16_t *dst = (uint16_t *) dst_map;
69      unsigned i;
70      for (i = 0; i < count; i++) {
71         dst[i] = (src[i] == restart_index) ? 0xffff : src[i];
72      }
73   }
74   else if (index_size == 2) {
75      uint16_t *src = (uint16_t *) src_map;
76      uint16_t *dst = (uint16_t *) dst_map;
77      unsigned i;
78      for (i = 0; i < count; i++) {
79         dst[i] = (src[i] == restart_index) ? 0xffff : src[i];
80      }
81   }
82   else {
83      uint32_t *src = (uint32_t *) src_map;
84      uint32_t *dst = (uint32_t *) dst_map;
85      unsigned i;
86      assert(index_size == 4);
87      for (i = 0; i < count; i++) {
88         dst[i] = (src[i] == restart_index) ? 0xffffffff : src[i];
89      }
90   }
91}
92
93/**
94 * Translate an index buffer for primitive restart.
95 * Create a new index buffer which is a copy of the original index buffer
96 * except that instances of 'restart_index' are converted to 0xffff or
97 * 0xffffffff.
98 * Also, index buffers using 1-byte indexes are converted to 2-byte indexes.
99 */
100enum pipe_error
101util_translate_prim_restart_ib(struct pipe_context *context,
102                               const struct pipe_draw_info *info,
103                               const struct pipe_draw_indirect_info *indirect_info,
104                               const struct pipe_draw_start_count_bias *draw,
105                               struct pipe_resource **dst_buffer)
106{
107   struct pipe_screen *screen = context->screen;
108   struct pipe_transfer *src_transfer = NULL, *dst_transfer = NULL;
109   void *src_map = NULL, *dst_map = NULL;
110   const unsigned src_index_size = info->index_size;
111   unsigned dst_index_size;
112   DrawElementsIndirectCommand indirect;
113   unsigned count = draw->count;
114   unsigned start = draw->start;
115
116   /* 1-byte indexes are converted to 2-byte indexes, 4-byte stays 4-byte */
117   dst_index_size = MAX2(2, info->index_size);
118   assert(dst_index_size == 2 || dst_index_size == 4);
119
120   if (indirect_info && indirect_info->buffer) {
121      indirect = read_indirect_elements(context, indirect_info);
122      count = indirect.count;
123      start = indirect.firstIndex;
124   }
125
126   /* Create new index buffer */
127   *dst_buffer = pipe_buffer_create(screen, PIPE_BIND_INDEX_BUFFER,
128                                    PIPE_USAGE_STREAM,
129                                    count * dst_index_size);
130   if (!*dst_buffer)
131      goto error;
132
133   /* Map new / dest index buffer */
134   dst_map = pipe_buffer_map(context, *dst_buffer,
135                             PIPE_MAP_WRITE, &dst_transfer);
136   if (!dst_map)
137      goto error;
138
139   if (info->has_user_indices)
140      src_map = (unsigned char*)info->index.user + start * src_index_size;
141   else
142      /* Map original / src index buffer */
143      src_map = pipe_buffer_map_range(context, info->index.resource,
144                                      start * src_index_size,
145                                      count * src_index_size,
146                                      PIPE_MAP_READ,
147                                      &src_transfer);
148   if (!src_map)
149      goto error;
150
151   util_translate_prim_restart_data(src_index_size, src_map, dst_map,
152                                    count, info->restart_index);
153
154   if (src_transfer)
155      pipe_buffer_unmap(context, src_transfer);
156   pipe_buffer_unmap(context, dst_transfer);
157
158   return PIPE_OK;
159
160error:
161   if (src_transfer)
162      pipe_buffer_unmap(context, src_transfer);
163   if (dst_transfer)
164      pipe_buffer_unmap(context, dst_transfer);
165   if (*dst_buffer)
166      pipe_resource_reference(dst_buffer, NULL);
167   return PIPE_ERROR_OUT_OF_MEMORY;
168}
169
170
171/** Helper structs for util_draw_vbo_without_prim_restart() */
172
173struct range_info {
174   struct pipe_draw_start_count_bias *draws;
175   unsigned count, max;
176   unsigned min_index, max_index;
177   unsigned total_index_count;
178};
179
180
181/**
182 * Helper function for util_draw_vbo_without_prim_restart()
183 * \return true for success, false if out of memory
184 */
185static boolean
186add_range(enum pipe_prim_type mode, struct range_info *info, unsigned start, unsigned count, unsigned index_bias)
187{
188   /* degenerate primitive: ignore */
189   if (!u_trim_pipe_prim(mode, (unsigned*)&count))
190      return TRUE;
191
192   if (info->max == 0) {
193      info->max = 10;
194      info->draws = MALLOC(info->max * sizeof(struct pipe_draw_start_count_bias));
195      if (!info->draws) {
196         return FALSE;
197      }
198   }
199   else if (info->count == info->max) {
200      /* grow the draws[] array */
201      info->draws = REALLOC(info->draws,
202                             info->max * sizeof(struct pipe_draw_start_count_bias),
203                             2 * info->max * sizeof(struct pipe_draw_start_count_bias));
204      if (!info->draws) {
205         return FALSE;
206      }
207
208      info->max *= 2;
209   }
210   info->min_index = MIN2(info->min_index, start);
211   info->max_index = MAX2(info->max_index, start + count - 1);
212
213   /* save the range */
214   info->draws[info->count].start = start;
215   info->draws[info->count].count = count;
216   info->draws[info->count].index_bias = index_bias;
217   info->count++;
218   info->total_index_count += count;
219
220   return TRUE;
221}
222
223struct pipe_draw_start_count_bias *
224util_prim_restart_convert_to_direct(const void *index_map,
225                                    const struct pipe_draw_info *info,
226                                    const struct pipe_draw_start_count_bias *draw,
227                                    unsigned *num_draws,
228                                    unsigned *min_index,
229                                    unsigned *max_index,
230                                    unsigned *total_index_count)
231{
232   struct range_info ranges = { .min_index = UINT32_MAX, 0 };
233   unsigned i, start, count;
234   ranges.min_index = UINT32_MAX;
235
236   assert(info->index_size);
237   assert(info->primitive_restart);
238
239#define SCAN_INDEXES(TYPE) \
240   for (i = 0; i <= draw->count; i++) { \
241      if (i == draw->count || \
242          ((const TYPE *) index_map)[i] == info->restart_index) { \
243         /* cut / restart */ \
244         if (count > 0) { \
245            if (!add_range(info->mode, &ranges, draw->start + start, count, draw->index_bias)) { \
246               return NULL; \
247            } \
248         } \
249         start = i + 1; \
250         count = 0; \
251      } \
252      else { \
253         count++; \
254      } \
255   }
256
257   start = 0;
258   count = 0;
259   switch (info->index_size) {
260   case 1:
261      SCAN_INDEXES(uint8_t);
262      break;
263   case 2:
264      SCAN_INDEXES(uint16_t);
265      break;
266   case 4:
267      SCAN_INDEXES(uint32_t);
268      break;
269   default:
270      assert(!"Bad index size");
271      return NULL;
272   }
273
274   *num_draws = ranges.count;
275   *min_index = ranges.min_index;
276   *max_index = ranges.max_index;
277   *total_index_count = ranges.total_index_count;
278   return ranges.draws;
279}
280
281/**
282 * Implement primitive restart by breaking an indexed primitive into
283 * pieces which do not contain restart indexes.  Each piece is then
284 * drawn by calling pipe_context::draw_vbo().
285 * \return PIPE_OK if no error, an error code otherwise.
286 */
287enum pipe_error
288util_draw_vbo_without_prim_restart(struct pipe_context *context,
289                                   const struct pipe_draw_info *info,
290                                   unsigned drawid_offset,
291                                   const struct pipe_draw_indirect_info *indirect_info,
292                                   const struct pipe_draw_start_count_bias *draw)
293{
294   const void *src_map;
295   struct pipe_draw_info new_info = *info;
296   struct pipe_draw_start_count_bias new_draw = *draw;
297   struct pipe_transfer *src_transfer = NULL;
298   DrawElementsIndirectCommand indirect;
299   struct pipe_draw_start_count_bias *direct_draws;
300   unsigned num_draws = 0;
301
302   assert(info->index_size);
303   assert(info->primitive_restart);
304
305   switch (info->index_size) {
306   case 1:
307   case 2:
308   case 4:
309      break;
310   default:
311      assert(!"Bad index size");
312      return PIPE_ERROR_BAD_INPUT;
313   }
314
315   if (indirect_info && indirect_info->buffer) {
316      indirect = read_indirect_elements(context, indirect_info);
317      new_draw.count = indirect.count;
318      new_draw.start = indirect.firstIndex;
319      new_info.instance_count = indirect.primCount;
320   }
321
322   /* Get pointer to the index data */
323   if (!info->has_user_indices) {
324      /* map the index buffer (only the range we need to scan) */
325      src_map = pipe_buffer_map_range(context, info->index.resource,
326                                      new_draw.start * info->index_size,
327                                      new_draw.count * info->index_size,
328                                      PIPE_MAP_READ,
329                                      &src_transfer);
330      if (!src_map) {
331         return PIPE_ERROR_OUT_OF_MEMORY;
332      }
333   }
334   else {
335      if (!info->index.user) {
336         debug_printf("User-space index buffer is null!");
337         return PIPE_ERROR_BAD_INPUT;
338      }
339      src_map = (const uint8_t *) info->index.user
340         + new_draw.start * info->index_size;
341   }
342
343   unsigned total_index_count;
344   direct_draws = util_prim_restart_convert_to_direct(src_map, &new_info, &new_draw, &num_draws,
345                                                      &new_info.min_index, &new_info.max_index,
346                                                      &total_index_count);
347   /* unmap index buffer */
348   if (src_transfer)
349      pipe_buffer_unmap(context, src_transfer);
350
351   new_info.primitive_restart = FALSE;
352   new_info.index_bounds_valid = true;
353   if (direct_draws)
354      context->draw_vbo(context, &new_info, drawid_offset, NULL, direct_draws, num_draws);
355   free(direct_draws);
356
357   return num_draws > 0 ? PIPE_OK : PIPE_ERROR_OUT_OF_MEMORY;
358}
359