1/*
2 * Copyright 2012 Red Hat Inc.
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 shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 *
24 */
25
26#include "pipe/p_context.h"
27#include "pipe/p_state.h"
28#include "util/u_inlines.h"
29#include "util/u_format.h"
30#include "translate/translate.h"
31
32#include "nouveau/nv_object.xml.h"
33#include "nv30-40_3d.xml.h"
34#include "nv30_context.h"
35#include "nv30_resource.h"
36
37struct push_context {
38   struct nouveau_pushbuf *push;
39
40   const void *idxbuf;
41
42   float edgeflag;
43   int edgeflag_attr;
44
45   uint32_t vertex_words;
46   uint32_t packet_vertex_limit;
47
48   struct translate *translate;
49
50   boolean primitive_restart;
51   uint32_t prim;
52   uint32_t restart_index;
53};
54
55static INLINE unsigned
56prim_restart_search_i08(uint8_t *elts, unsigned push, uint8_t index)
57{
58   unsigned i;
59   for (i = 0; i < push; ++i)
60      if (elts[i] == index)
61         break;
62   return i;
63}
64
65static INLINE unsigned
66prim_restart_search_i16(uint16_t *elts, unsigned push, uint16_t index)
67{
68   unsigned i;
69   for (i = 0; i < push; ++i)
70      if (elts[i] == index)
71         break;
72   return i;
73}
74
75static INLINE unsigned
76prim_restart_search_i32(uint32_t *elts, unsigned push, uint32_t index)
77{
78   unsigned i;
79   for (i = 0; i < push; ++i)
80      if (elts[i] == index)
81         break;
82   return i;
83}
84
85static void
86emit_vertices_i08(struct push_context *ctx, unsigned start, unsigned count)
87{
88   uint8_t *elts = (uint8_t *)ctx->idxbuf + start;
89
90   while (count) {
91      unsigned push = MIN2(count, ctx->packet_vertex_limit);
92      unsigned size, nr;
93
94      nr = push;
95      if (ctx->primitive_restart)
96         nr = prim_restart_search_i08(elts, push, ctx->restart_index);
97
98      size = ctx->vertex_words * nr;
99
100      BEGIN_NI04(ctx->push, NV30_3D(VERTEX_DATA), size);
101
102      ctx->translate->run_elts8(ctx->translate, elts, nr, 0, ctx->push->cur);
103
104      ctx->push->cur += size;
105      count -= nr;
106      elts += nr;
107
108      if (nr != push) {
109         BEGIN_NV04(ctx->push, NV30_3D(VB_ELEMENT_U32), 1);
110         PUSH_DATA (ctx->push, ctx->restart_index);
111         count--;
112         elts++;
113      }
114   }
115}
116
117static void
118emit_vertices_i16(struct push_context *ctx, unsigned start, unsigned count)
119{
120   uint16_t *elts = (uint16_t *)ctx->idxbuf + start;
121
122   while (count) {
123      unsigned push = MIN2(count, ctx->packet_vertex_limit);
124      unsigned size, nr;
125
126      nr = push;
127      if (ctx->primitive_restart)
128         nr = prim_restart_search_i16(elts, push, ctx->restart_index);
129
130      size = ctx->vertex_words * nr;
131
132      BEGIN_NI04(ctx->push, NV30_3D(VERTEX_DATA), size);
133
134      ctx->translate->run_elts16(ctx->translate, elts, nr, 0, ctx->push->cur);
135
136      ctx->push->cur += size;
137      count -= nr;
138      elts += nr;
139
140      if (nr != push) {
141         BEGIN_NV04(ctx->push, NV30_3D(VB_ELEMENT_U32), 1);
142         PUSH_DATA (ctx->push, ctx->restart_index);
143         count--;
144         elts++;
145      }
146   }
147}
148
149static void
150emit_vertices_i32(struct push_context *ctx, unsigned start, unsigned count)
151{
152   uint32_t *elts = (uint32_t *)ctx->idxbuf + start;
153
154   while (count) {
155      unsigned push = MIN2(count, ctx->packet_vertex_limit);
156      unsigned size, nr;
157
158      nr = push;
159      if (ctx->primitive_restart)
160         nr = prim_restart_search_i32(elts, push, ctx->restart_index);
161
162      size = ctx->vertex_words * nr;
163
164      BEGIN_NI04(ctx->push, NV30_3D(VERTEX_DATA), size);
165
166      ctx->translate->run_elts(ctx->translate, elts, nr, 0, ctx->push->cur);
167
168      ctx->push->cur += size;
169      count -= nr;
170      elts += nr;
171
172      if (nr != push) {
173         BEGIN_NV04(ctx->push, NV30_3D(VB_ELEMENT_U32), 1);
174         PUSH_DATA (ctx->push, ctx->restart_index);
175         count--;
176         elts++;
177      }
178   }
179}
180
181static void
182emit_vertices_seq(struct push_context *ctx, unsigned start, unsigned count)
183{
184   while (count) {
185      unsigned push = MIN2(count, ctx->packet_vertex_limit);
186      unsigned size = ctx->vertex_words * push;
187
188      BEGIN_NI04(ctx->push, NV30_3D(VERTEX_DATA), size);
189
190      ctx->translate->run(ctx->translate, start, push, 0, ctx->push->cur);
191      ctx->push->cur += size;
192      count -= push;
193      start += push;
194   }
195}
196
197void
198nv30_push_vbo(struct nv30_context *nv30, const struct pipe_draw_info *info)
199{
200   struct push_context ctx;
201   unsigned i, index_size;
202   boolean apply_bias = info->indexed && info->index_bias;
203
204   ctx.push = nv30->base.pushbuf;
205   ctx.translate = nv30->vertex->translate;
206   ctx.packet_vertex_limit = nv30->vertex->vtx_per_packet_max;
207   ctx.vertex_words = nv30->vertex->vtx_size;
208
209   for (i = 0; i < nv30->num_vtxbufs; ++i) {
210      uint8_t *data;
211      struct pipe_vertex_buffer *vb = &nv30->vtxbuf[i];
212      struct nv04_resource *res = nv04_resource(vb->buffer);
213
214      data = nouveau_resource_map_offset(&nv30->base, res,
215                                         vb->buffer_offset, NOUVEAU_BO_RD);
216
217      if (apply_bias)
218         data += info->index_bias * vb->stride;
219
220      ctx.translate->set_buffer(ctx.translate, i, data, vb->stride, ~0);
221   }
222
223   if (info->indexed) {
224      if (nv30->idxbuf.buffer)
225         ctx.idxbuf = nouveau_resource_map_offset(&nv30->base,
226            nv04_resource(nv30->idxbuf.buffer), nv30->idxbuf.offset,
227            NOUVEAU_BO_RD);
228      else
229         ctx.idxbuf = nv30->idxbuf.user_buffer;
230      if (!ctx.idxbuf) {
231         nv30_state_release(nv30);
232         return;
233      }
234      index_size = nv30->idxbuf.index_size;
235      ctx.primitive_restart = info->primitive_restart;
236      ctx.restart_index = info->restart_index;
237   } else {
238      ctx.idxbuf = NULL;
239      index_size = 0;
240      ctx.primitive_restart = FALSE;
241      ctx.restart_index = 0;
242   }
243
244   if (nv30->screen->eng3d->oclass >= NV40_3D_CLASS) {
245      BEGIN_NV04(ctx.push, NV40_3D(PRIM_RESTART_ENABLE), 2);
246      PUSH_DATA (ctx.push, info->primitive_restart);
247      PUSH_DATA (ctx.push, info->restart_index);
248      nv30->state.prim_restart = info->primitive_restart;
249   }
250
251   ctx.prim = nv30_prim_gl(info->mode);
252
253   PUSH_RESET(ctx.push, BUFCTX_IDXBUF);
254   BEGIN_NV04(ctx.push, NV30_3D(VERTEX_BEGIN_END), 1);
255   PUSH_DATA (ctx.push, ctx.prim);
256   switch (index_size) {
257   case 0:
258      emit_vertices_seq(&ctx, info->start, info->count);
259      break;
260   case 1:
261      emit_vertices_i08(&ctx, info->start, info->count);
262      break;
263   case 2:
264      emit_vertices_i16(&ctx, info->start, info->count);
265      break;
266   case 4:
267      emit_vertices_i32(&ctx, info->start, info->count);
268      break;
269   default:
270      assert(0);
271      break;
272   }
273   BEGIN_NV04(ctx.push, NV30_3D(VERTEX_BEGIN_END), 1);
274   PUSH_DATA (ctx.push, NV30_3D_VERTEX_BEGIN_END_STOP);
275
276   if (info->indexed)
277      nouveau_resource_unmap(nv04_resource(nv30->idxbuf.buffer));
278
279   for (i = 0; i < nv30->num_vtxbufs; ++i)
280      nouveau_resource_unmap(nv04_resource(nv30->vtxbuf[i].buffer));
281
282   nv30_state_release(nv30);
283}
284