2 * Copyright (C) 2009-2010 Francisco Jerez.
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a 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, sublicense, 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:
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #include "nouveau_bufferobj.h"
28 #include "nouveau_util.h"
30 #include "main/bufferobj.h"
31 #include "main/image.h"
33 /* Arbitrary pushbuf length we can assume we can get with a single
35 #define PUSHBUF_DWORDS 65536
37 /* Functions to set up struct nouveau_array_state from something like
38 * a GL array or index buffer. */
41 vbo_init_array(struct nouveau_array_state
*a
, int attr
, int stride
,
42 int fields
, int type
, struct gl_buffer_object
*obj
,
43 const void *ptr
, GLboolean map
)
50 if (_mesa_is_bufferobj(obj
)) {
51 nouveau_bo_ref(to_nouveau_bufferobj(obj
)->bo
, &a
->bo
);
52 a
->offset
= (intptr_t)ptr
;
55 nouveau_bo_map(a
->bo
, NOUVEAU_BO_RD
);
56 a
->buf
= a
->bo
->map
+ a
->offset
;
62 nouveau_bo_ref(NULL
, &a
->bo
);
72 get_array_extract(a
, &a
->extract_u
, &a
->extract_f
);
76 vbo_deinit_array(struct nouveau_array_state
*a
)
80 nouveau_bo_unmap(a
->bo
);
81 nouveau_bo_ref(NULL
, &a
->bo
);
89 get_array_stride(struct gl_context
*ctx
, const struct gl_client_array
*a
)
91 struct nouveau_render_state
*render
= to_render_state(ctx
);
93 if (render
->mode
== VBO
&& !_mesa_is_bufferobj(a
->BufferObj
))
94 /* Pack client buffers. */
95 return align(_mesa_sizeof_type(a
->Type
) * a
->Size
, 4);
101 vbo_init_arrays(struct gl_context
*ctx
, const struct _mesa_index_buffer
*ib
,
102 const struct gl_client_array
**arrays
)
104 struct nouveau_render_state
*render
= to_render_state(ctx
);
105 GLboolean imm
= (render
->mode
== IMM
);
109 vbo_init_array(&render
->ib
, 0, 0, ib
->count
, ib
->type
,
110 ib
->obj
, ib
->ptr
, GL_TRUE
);
112 FOR_EACH_BOUND_ATTR(render
, i
, attr
) {
113 const struct gl_client_array
*array
= arrays
[attr
];
115 vbo_init_array(&render
->attrs
[attr
], attr
,
116 get_array_stride(ctx
, array
),
117 array
->Size
, array
->Type
,
124 vbo_deinit_arrays(struct gl_context
*ctx
, const struct _mesa_index_buffer
*ib
,
125 const struct gl_client_array
**arrays
)
127 struct nouveau_render_state
*render
= to_render_state(ctx
);
131 vbo_deinit_array(&render
->ib
);
133 FOR_EACH_BOUND_ATTR(render
, i
, attr
) {
134 struct nouveau_array_state
*a
= &render
->attrs
[attr
];
140 render
->attr_count
= 0;
141 context_bctx(ctx
, VERTEX
);
144 /* Make some rendering decisions from the GL context. */
147 vbo_choose_render_mode(struct gl_context
*ctx
, const struct gl_client_array
**arrays
)
149 struct nouveau_render_state
*render
= to_render_state(ctx
);
154 if (ctx
->Light
.Enabled
) {
155 for (i
= 0; i
< MAT_ATTRIB_MAX
; i
++) {
156 if (arrays
[VERT_ATTRIB_GENERIC0
+ i
]->StrideB
) {
165 vbo_emit_attr(struct gl_context
*ctx
, const struct gl_client_array
**arrays
, int attr
)
167 struct nouveau_channel
*chan
= context_chan(ctx
);
168 struct nouveau_render_state
*render
= to_render_state(ctx
);
169 const struct gl_client_array
*array
= arrays
[attr
];
170 struct nouveau_array_state
*a
= &render
->attrs
[attr
];
173 if (!array
->StrideB
) {
174 if (attr
>= VERT_ATTRIB_GENERIC0
)
175 /* nouveau_update_state takes care of materials. */
178 /* Constant attribute. */
179 vbo_init_array(a
, attr
, array
->StrideB
, array
->Size
,
180 array
->Type
, array
->BufferObj
, array
->Ptr
,
186 /* Varying attribute. */
187 struct nouveau_attr_info
*info
= &TAG(vertex_attrs
)[attr
];
189 if (render
->mode
== VBO
) {
190 render
->map
[info
->vbo_index
] = attr
;
191 render
->vertex_size
+= array
->_ElementSize
;
192 render
->attr_count
= MAX2(render
->attr_count
,
193 info
->vbo_index
+ 1);
195 render
->map
[render
->attr_count
++] = attr
;
196 render
->vertex_size
+= 4 * info
->imm_fields
;
202 #define MAT(a) (VERT_ATTRIB_GENERIC0 + MAT_ATTRIB_##a)
205 vbo_choose_attrs(struct gl_context
*ctx
, const struct gl_client_array
**arrays
)
207 struct nouveau_render_state
*render
= to_render_state(ctx
);
210 /* Reset the vertex size. */
211 render
->vertex_size
= 0;
212 render
->attr_count
= 0;
214 vbo_emit_attr(ctx
, arrays
, VERT_ATTRIB_COLOR0
);
215 if (ctx
->Fog
.ColorSumEnabled
&& !ctx
->Light
.Enabled
)
216 vbo_emit_attr(ctx
, arrays
, VERT_ATTRIB_COLOR1
);
218 for (i
= 0; i
< ctx
->Const
.MaxTextureCoordUnits
; i
++) {
219 if (ctx
->Texture
._EnabledCoordUnits
& (1 << i
))
220 vbo_emit_attr(ctx
, arrays
, VERT_ATTRIB_TEX0
+ i
);
223 if (ctx
->Fog
.Enabled
&& ctx
->Fog
.FogCoordinateSource
== GL_FOG_COORD
)
224 vbo_emit_attr(ctx
, arrays
, VERT_ATTRIB_FOG
);
226 if (ctx
->Light
.Enabled
||
227 (ctx
->Texture
._GenFlags
& TEXGEN_NEED_NORMALS
))
228 vbo_emit_attr(ctx
, arrays
, VERT_ATTRIB_NORMAL
);
230 if (ctx
->Light
.Enabled
) {
231 vbo_emit_attr(ctx
, arrays
, MAT(FRONT_AMBIENT
));
232 vbo_emit_attr(ctx
, arrays
, MAT(FRONT_DIFFUSE
));
233 vbo_emit_attr(ctx
, arrays
, MAT(FRONT_SPECULAR
));
234 vbo_emit_attr(ctx
, arrays
, MAT(FRONT_SHININESS
));
236 if (ctx
->Light
.Model
.TwoSide
) {
237 vbo_emit_attr(ctx
, arrays
, MAT(BACK_AMBIENT
));
238 vbo_emit_attr(ctx
, arrays
, MAT(BACK_DIFFUSE
));
239 vbo_emit_attr(ctx
, arrays
, MAT(BACK_SPECULAR
));
240 vbo_emit_attr(ctx
, arrays
, MAT(BACK_SHININESS
));
244 vbo_emit_attr(ctx
, arrays
, VERT_ATTRIB_POS
);
248 get_max_client_stride(struct gl_context
*ctx
, const struct gl_client_array
**arrays
)
250 struct nouveau_render_state
*render
= to_render_state(ctx
);
253 FOR_EACH_BOUND_ATTR(render
, i
, attr
) {
254 const struct gl_client_array
*a
= arrays
[attr
];
256 if (!_mesa_is_bufferobj(a
->BufferObj
))
257 s
= MAX2(s
, get_array_stride(ctx
, a
));
264 TAG(vbo_render_prims
)(struct gl_context
*ctx
, const struct gl_client_array
**arrays
,
265 const struct _mesa_prim
*prims
, GLuint nr_prims
,
266 const struct _mesa_index_buffer
*ib
,
267 GLboolean index_bounds_valid
,
268 GLuint min_index
, GLuint max_index
);
271 vbo_maybe_split(struct gl_context
*ctx
, const struct gl_client_array
**arrays
,
272 const struct _mesa_prim
*prims
, GLuint nr_prims
,
273 const struct _mesa_index_buffer
*ib
,
274 GLuint min_index
, GLuint max_index
)
276 struct nouveau_context
*nctx
= to_nouveau_context(ctx
);
277 struct nouveau_render_state
*render
= to_render_state(ctx
);
278 unsigned pushbuf_avail
= PUSHBUF_DWORDS
- 2 * (nctx
->bo
.count
+
280 vert_avail
= get_max_vertices(ctx
, NULL
, pushbuf_avail
),
281 idx_avail
= get_max_vertices(ctx
, ib
, pushbuf_avail
);
284 /* Try to keep client buffers smaller than the scratch BOs. */
285 if (render
->mode
== VBO
&&
286 (stride
= get_max_client_stride(ctx
, arrays
)))
287 vert_avail
= MIN2(vert_avail
,
288 NOUVEAU_SCRATCH_SIZE
/ stride
);
290 if (max_index
- min_index
> vert_avail
||
291 (ib
&& ib
->count
> idx_avail
)) {
292 struct split_limits limits
= {
293 .max_verts
= vert_avail
,
294 .max_indices
= idx_avail
,
298 vbo_split_prims(ctx
, arrays
, prims
, nr_prims
, ib
, min_index
,
299 max_index
, TAG(vbo_render_prims
), &limits
);
306 /* VBO rendering path. */
309 vbo_bind_vertices(struct gl_context
*ctx
, const struct gl_client_array
**arrays
,
310 GLint basevertex
, GLuint min_index
, GLuint max_index
)
312 struct nouveau_render_state
*render
= to_render_state(ctx
);
315 FOR_EACH_BOUND_ATTR(render
, i
, attr
) {
316 const struct gl_client_array
*array
= arrays
[attr
];
317 struct nouveau_array_state
*a
= &render
->attrs
[attr
];
318 unsigned delta
= (basevertex
+ min_index
)
322 /* Array in a buffer obj. */
323 a
->offset
= (intptr_t)array
->Ptr
+ delta
;
325 int j
, n
= max_index
- min_index
+ 1;
326 char *sp
= (char *)array
->Ptr
+ delta
;
327 char *dp
= nouveau_get_scratch(
328 ctx
, n
* a
->stride
, &a
->bo
, &a
->offset
);
330 /* Array in client memory, move it to
331 * a scratch buffer obj. */
332 for (j
= 0; j
< n
; j
++)
333 memcpy(dp
+ j
* a
->stride
,
334 sp
+ j
* array
->StrideB
,
339 TAG(render_bind_vertices
)(ctx
);
343 vbo_draw_vbo(struct gl_context
*ctx
, const struct gl_client_array
**arrays
,
344 const struct _mesa_prim
*prims
, GLuint nr_prims
,
345 const struct _mesa_index_buffer
*ib
, GLuint min_index
,
348 struct nouveau_channel
*chan
= context_chan(ctx
);
350 int delta
= -min_index
, basevertex
= 0, i
;
353 get_array_dispatch(&to_render_state(ctx
)->ib
, &dispatch
);
355 TAG(render_set_format
)(ctx
);
357 for (i
= 0; i
< nr_prims
; i
++) {
358 unsigned start
= prims
[i
].start
,
359 count
= prims
[i
].count
;
361 if (i
== 0 || basevertex
!= prims
[i
].basevertex
) {
362 basevertex
= prims
[i
].basevertex
;
363 vbo_bind_vertices(ctx
, arrays
, basevertex
,
364 min_index
, max_index
);
367 if (count
> get_max_vertices(ctx
, ib
, AVAIL_RING(chan
)))
368 WAIT_RING(chan
, PUSHBUF_DWORDS
);
370 BATCH_BEGIN(nvgl_primitive(prims
[i
].mode
));
371 dispatch(ctx
, start
, delta
, count
);
376 /* Immediate rendering path. */
379 extract_id(struct nouveau_array_state
*a
, int i
, int j
)
385 vbo_draw_imm(struct gl_context
*ctx
, const struct gl_client_array
**arrays
,
386 const struct _mesa_prim
*prims
, GLuint nr_prims
,
387 const struct _mesa_index_buffer
*ib
, GLuint min_index
,
390 struct nouveau_render_state
*render
= to_render_state(ctx
);
391 struct nouveau_channel
*chan
= context_chan(ctx
);
392 extract_u_t extract
= ib
? render
->ib
.extract_u
: extract_id
;
396 for (i
= 0; i
< nr_prims
; i
++) {
397 unsigned start
= prims
[i
].start
,
398 end
= start
+ prims
[i
].count
;
400 if (prims
[i
].count
> get_max_vertices(ctx
, ib
,
402 WAIT_RING(chan
, PUSHBUF_DWORDS
);
404 BATCH_BEGIN(nvgl_primitive(prims
[i
].mode
));
406 for (; start
< end
; start
++) {
407 j
= prims
[i
].basevertex
+
408 extract(&render
->ib
, 0, start
);
410 FOR_EACH_BOUND_ATTR(render
, k
, attr
)
411 EMIT_IMM(ctx
, &render
->attrs
[attr
], j
);
418 /* draw_prims entry point when we're doing hw-tnl. */
421 TAG(vbo_render_prims
)(struct gl_context
*ctx
, const struct gl_client_array
**arrays
,
422 const struct _mesa_prim
*prims
, GLuint nr_prims
,
423 const struct _mesa_index_buffer
*ib
,
424 GLboolean index_bounds_valid
,
425 GLuint min_index
, GLuint max_index
)
427 struct nouveau_render_state
*render
= to_render_state(ctx
);
429 if (!index_bounds_valid
)
430 vbo_get_minmax_index(ctx
, prims
, ib
, &min_index
, &max_index
);
432 vbo_choose_render_mode(ctx
, arrays
);
433 vbo_choose_attrs(ctx
, arrays
);
435 if (vbo_maybe_split(ctx
, arrays
, prims
, nr_prims
, ib
, min_index
,
439 vbo_init_arrays(ctx
, ib
, arrays
);
441 if (render
->mode
== VBO
)
442 vbo_draw_vbo(ctx
, arrays
, prims
, nr_prims
, ib
, min_index
,
445 vbo_draw_imm(ctx
, arrays
, prims
, nr_prims
, ib
, min_index
,
448 vbo_deinit_arrays(ctx
, ib
, arrays
);