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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE 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 "nv_object.xml.h"
33#include "nv30/nv30-40_3d.xml.h"
34#include "nv30/nv30_context.h"
35#include "nv30/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   bool 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, 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, 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, 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, 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   bool 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      if (!vb->buffer && !vb->user_buffer) {
215         continue;
216      }
217
218      data = nouveau_resource_map_offset(&nv30->base, res,
219                                         vb->buffer_offset, NOUVEAU_BO_RD);
220
221      if (apply_bias)
222         data += info->index_bias * vb->stride;
223
224      ctx.translate->set_buffer(ctx.translate, i, data, vb->stride, ~0);
225   }
226
227   if (info->indexed) {
228      if (nv30->idxbuf.buffer)
229         ctx.idxbuf = nouveau_resource_map_offset(&nv30->base,
230            nv04_resource(nv30->idxbuf.buffer), nv30->idxbuf.offset,
231            NOUVEAU_BO_RD);
232      else
233         ctx.idxbuf = nv30->idxbuf.user_buffer;
234      if (!ctx.idxbuf) {
235         nv30_state_release(nv30);
236         return;
237      }
238      index_size = nv30->idxbuf.index_size;
239      ctx.primitive_restart = info->primitive_restart;
240      ctx.restart_index = info->restart_index;
241   } else {
242      ctx.idxbuf = NULL;
243      index_size = 0;
244      ctx.primitive_restart = false;
245      ctx.restart_index = 0;
246   }
247
248   if (nv30->screen->eng3d->oclass >= NV40_3D_CLASS) {
249      BEGIN_NV04(ctx.push, NV40_3D(PRIM_RESTART_ENABLE), 2);
250      PUSH_DATA (ctx.push, info->primitive_restart);
251      PUSH_DATA (ctx.push, info->restart_index);
252      nv30->state.prim_restart = info->primitive_restart;
253   }
254
255   ctx.prim = nv30_prim_gl(info->mode);
256
257   PUSH_RESET(ctx.push, BUFCTX_IDXBUF);
258   BEGIN_NV04(ctx.push, NV30_3D(VERTEX_BEGIN_END), 1);
259   PUSH_DATA (ctx.push, ctx.prim);
260   switch (index_size) {
261   case 0:
262      emit_vertices_seq(&ctx, info->start, info->count);
263      break;
264   case 1:
265      emit_vertices_i08(&ctx, info->start, info->count);
266      break;
267   case 2:
268      emit_vertices_i16(&ctx, info->start, info->count);
269      break;
270   case 4:
271      emit_vertices_i32(&ctx, info->start, info->count);
272      break;
273   default:
274      assert(0);
275      break;
276   }
277   BEGIN_NV04(ctx.push, NV30_3D(VERTEX_BEGIN_END), 1);
278   PUSH_DATA (ctx.push, NV30_3D_VERTEX_BEGIN_END_STOP);
279
280   if (info->indexed)
281      nouveau_resource_unmap(nv04_resource(nv30->idxbuf.buffer));
282
283   for (i = 0; i < nv30->num_vtxbufs; ++i) {
284      if (nv30->vtxbuf[i].buffer) {
285         nouveau_resource_unmap(nv04_resource(nv30->vtxbuf[i].buffer));
286      }
287   }
288
289   nv30_state_release(nv30);
290}
291