1 /**************************************************************************
2
3 Copyright 2002-2008 VMware, Inc.
4
5 All Rights Reserved.
6
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the "Software"),
9 to deal in the Software without restriction, including without limitation
10 on the rights to use, copy, modify, merge, publish, distribute, sub
11 license, and/or sell copies of the Software, and to permit persons to whom
12 the Software is furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice (including the next
15 paragraph) shall be included in all copies or substantial portions of the
16 Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 VMWARE AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 **************************************************************************/
27
28 /*
29 * Authors:
30 * Keith Whitwell <keithw@vmware.com>
31 */
32
33 #include "main/glheader.h"
34 #include "main/bufferobj.h"
35 #include "main/context.h"
36 #include "main/macros.h"
37 #include "main/vtxfmt.h"
38 #include "main/dlist.h"
39 #include "main/eval.h"
40 #include "main/state.h"
41 #include "main/light.h"
42 #include "main/api_arrayelt.h"
43 #include "main/draw_validate.h"
44 #include "main/dispatch.h"
45 #include "util/bitscan.h"
46 #include "util/u_memory.h"
47
48 #include "vbo_noop.h"
49 #include "vbo_private.h"
50
51
52 /** ID/name for immediate-mode VBO */
53 #define IMM_BUFFER_NAME 0xaabbccdd
54
55
56 static void GLAPIENTRY
57 vbo_exec_Materialfv(GLenum face, GLenum pname, const GLfloat *params);
58
59 static void GLAPIENTRY
60 vbo_exec_EvalCoord1f(GLfloat u);
61
62 static void GLAPIENTRY
63 vbo_exec_EvalCoord2f(GLfloat u, GLfloat v);
64
65
66 static void
67 vbo_reset_all_attr(struct vbo_exec_context *exec);
68
69
70 /**
71 * Close off the last primitive, execute the buffer, restart the
72 * primitive. This is called when we fill a vertex buffer before
73 * hitting glEnd.
74 */
75 static void
vbo_exec_wrap_buffers(struct vbo_exec_context * exec)76 vbo_exec_wrap_buffers(struct vbo_exec_context *exec)
77 {
78 if (exec->vtx.prim_count == 0) {
79 exec->vtx.copied.nr = 0;
80 exec->vtx.vert_count = 0;
81 exec->vtx.buffer_ptr = exec->vtx.buffer_map;
82 }
83 else {
84 struct _mesa_prim *last_prim = &exec->vtx.prim[exec->vtx.prim_count - 1];
85 const GLuint last_begin = last_prim->begin;
86 GLuint last_count;
87
88 if (_mesa_inside_begin_end(exec->ctx)) {
89 last_prim->count = exec->vtx.vert_count - last_prim->start;
90 }
91
92 last_count = last_prim->count;
93
94 /* Special handling for wrapping GL_LINE_LOOP */
95 if (last_prim->mode == GL_LINE_LOOP &&
96 last_count > 0 &&
97 !last_prim->end) {
98 /* draw this section of the incomplete line loop as a line strip */
99 last_prim->mode = GL_LINE_STRIP;
100 if (!last_prim->begin) {
101 /* This is not the first section of the line loop, so don't
102 * draw the 0th vertex. We're saving it until we draw the
103 * very last section of the loop.
104 */
105 last_prim->start++;
106 last_prim->count--;
107 }
108 }
109
110 /* Execute the buffer and save copied vertices.
111 */
112 if (exec->vtx.vert_count)
113 vbo_exec_vtx_flush(exec);
114 else {
115 exec->vtx.prim_count = 0;
116 exec->vtx.copied.nr = 0;
117 }
118
119 /* Emit a glBegin to start the new list.
120 */
121 assert(exec->vtx.prim_count == 0);
122
123 if (_mesa_inside_begin_end(exec->ctx)) {
124 exec->vtx.prim[0].mode = exec->ctx->Driver.CurrentExecPrimitive;
125 exec->vtx.prim[0].begin = 0;
126 exec->vtx.prim[0].end = 0;
127 exec->vtx.prim[0].start = 0;
128 exec->vtx.prim[0].count = 0;
129 exec->vtx.prim_count++;
130
131 if (exec->vtx.copied.nr == last_count)
132 exec->vtx.prim[0].begin = last_begin;
133 }
134 }
135 }
136
137
138 /**
139 * Deal with buffer wrapping where provoked by the vertex buffer
140 * filling up, as opposed to upgrade_vertex().
141 */
142 static void
vbo_exec_vtx_wrap(struct vbo_exec_context * exec)143 vbo_exec_vtx_wrap(struct vbo_exec_context *exec)
144 {
145 unsigned numComponents;
146
147 /* Run pipeline on current vertices, copy wrapped vertices
148 * to exec->vtx.copied.
149 */
150 vbo_exec_wrap_buffers(exec);
151
152 if (!exec->vtx.buffer_ptr) {
153 /* probably ran out of memory earlier when allocating the VBO */
154 return;
155 }
156
157 /* Copy stored stored vertices to start of new list.
158 */
159 assert(exec->vtx.max_vert - exec->vtx.vert_count > exec->vtx.copied.nr);
160
161 numComponents = exec->vtx.copied.nr * exec->vtx.vertex_size;
162 memcpy(exec->vtx.buffer_ptr,
163 exec->vtx.copied.buffer,
164 numComponents * sizeof(fi_type));
165 exec->vtx.buffer_ptr += numComponents;
166 exec->vtx.vert_count += exec->vtx.copied.nr;
167
168 exec->vtx.copied.nr = 0;
169 }
170
171
172 /**
173 * Copy the active vertex's values to the ctx->Current fields.
174 */
175 static void
vbo_exec_copy_to_current(struct vbo_exec_context * exec)176 vbo_exec_copy_to_current(struct vbo_exec_context *exec)
177 {
178 struct gl_context *ctx = exec->ctx;
179 struct vbo_context *vbo = vbo_context(ctx);
180 GLbitfield64 enabled = exec->vtx.enabled & (~BITFIELD64_BIT(VBO_ATTRIB_POS));
181
182 while (enabled) {
183 const int i = u_bit_scan64(&enabled);
184
185 /* Note: the exec->vtx.current[i] pointers point into the
186 * ctx->Current.Attrib and ctx->Light.Material.Attrib arrays.
187 */
188 GLfloat *current = (GLfloat *)vbo->current[i].Ptr;
189 fi_type tmp[8]; /* space for doubles */
190 int dmul = 1;
191
192 if (exec->vtx.attr[i].type == GL_DOUBLE ||
193 exec->vtx.attr[i].type == GL_UNSIGNED_INT64_ARB)
194 dmul = 2;
195
196 assert(exec->vtx.attr[i].size);
197
198 if (exec->vtx.attr[i].type == GL_DOUBLE ||
199 exec->vtx.attr[i].type == GL_UNSIGNED_INT64_ARB) {
200 memset(tmp, 0, sizeof(tmp));
201 memcpy(tmp, exec->vtx.attrptr[i], exec->vtx.attr[i].size * sizeof(GLfloat));
202 } else {
203 COPY_CLEAN_4V_TYPE_AS_UNION(tmp,
204 exec->vtx.attr[i].size,
205 exec->vtx.attrptr[i],
206 exec->vtx.attr[i].type);
207 }
208
209 if (exec->vtx.attr[i].type != vbo->current[i].Format.Type ||
210 memcmp(current, tmp, 4 * sizeof(GLfloat) * dmul) != 0) {
211 memcpy(current, tmp, 4 * sizeof(GLfloat) * dmul);
212
213 /* Given that we explicitly state size here, there is no need
214 * for the COPY_CLEAN above, could just copy 16 bytes and be
215 * done. The only problem is when Mesa accesses ctx->Current
216 * directly.
217 */
218 /* Size here is in components - not bytes */
219 vbo_set_vertex_format(&vbo->current[i].Format,
220 exec->vtx.attr[i].size / dmul,
221 exec->vtx.attr[i].type);
222
223 /* This triggers rather too much recalculation of Mesa state
224 * that doesn't get used (eg light positions).
225 */
226 if (i >= VBO_ATTRIB_MAT_FRONT_AMBIENT &&
227 i <= VBO_ATTRIB_MAT_BACK_INDEXES)
228 ctx->NewState |= _NEW_LIGHT;
229
230 ctx->NewState |= _NEW_CURRENT_ATTRIB;
231 }
232 }
233
234 /* Colormaterial -- this kindof sucks.
235 */
236 if (ctx->Light.ColorMaterialEnabled &&
237 exec->vtx.attr[VBO_ATTRIB_COLOR0].size) {
238 _mesa_update_color_material(ctx,
239 ctx->Current.Attrib[VBO_ATTRIB_COLOR0]);
240 }
241 }
242
243
244 /**
245 * Flush existing data, set new attrib size, replay copied vertices.
246 * This is called when we transition from a small vertex attribute size
247 * to a larger one. Ex: glTexCoord2f -> glTexCoord4f.
248 * We need to go back over the previous 2-component texcoords and insert
249 * zero and one values.
250 * \param attr VBO_ATTRIB_x vertex attribute value
251 */
252 static void
vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context * exec,GLuint attr,GLuint newSize,GLenum newType)253 vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context *exec,
254 GLuint attr, GLuint newSize, GLenum newType)
255 {
256 struct gl_context *ctx = exec->ctx;
257 struct vbo_context *vbo = vbo_context(ctx);
258 const GLint lastcount = exec->vtx.vert_count;
259 fi_type *old_attrptr[VBO_ATTRIB_MAX];
260 const GLuint old_vtx_size_no_pos = exec->vtx.vertex_size_no_pos;
261 const GLuint old_vtx_size = exec->vtx.vertex_size; /* floats per vertex */
262 const GLuint oldSize = exec->vtx.attr[attr].size;
263 GLuint i;
264
265 assert(attr < VBO_ATTRIB_MAX);
266
267 /* Run pipeline on current vertices, copy wrapped vertices
268 * to exec->vtx.copied.
269 */
270 vbo_exec_wrap_buffers(exec);
271
272 if (unlikely(exec->vtx.copied.nr)) {
273 /* We're in the middle of a primitive, keep the old vertex
274 * format around to be able to translate the copied vertices to
275 * the new format.
276 */
277 memcpy(old_attrptr, exec->vtx.attrptr, sizeof(old_attrptr));
278 }
279
280 /* Heuristic: Attempt to isolate attributes received outside
281 * begin/end so that they don't bloat the vertices.
282 */
283 if (!_mesa_inside_begin_end(ctx) &&
284 !oldSize && lastcount > 8 && exec->vtx.vertex_size) {
285 vbo_exec_copy_to_current(exec);
286 vbo_reset_all_attr(exec);
287 }
288
289 /* Fix up sizes:
290 */
291 exec->vtx.attr[attr].size = newSize;
292 exec->vtx.attr[attr].active_size = newSize;
293 exec->vtx.attr[attr].type = newType;
294 exec->vtx.vertex_size += newSize - oldSize;
295 exec->vtx.vertex_size_no_pos = exec->vtx.vertex_size - exec->vtx.attr[0].size;
296 exec->vtx.max_vert = vbo_compute_max_verts(exec);
297 exec->vtx.vert_count = 0;
298 exec->vtx.buffer_ptr = exec->vtx.buffer_map;
299 exec->vtx.enabled |= BITFIELD64_BIT(attr);
300
301 if (attr != 0) {
302 if (unlikely(oldSize)) {
303 unsigned offset = exec->vtx.attrptr[attr] - exec->vtx.vertex;
304
305 /* If there are attribs after the resized attrib... */
306 if (offset + oldSize < old_vtx_size_no_pos) {
307 int size_diff = newSize - oldSize;
308 fi_type *old_first = exec->vtx.attrptr[attr] + oldSize;
309 fi_type *new_first = exec->vtx.attrptr[attr] + newSize;
310 fi_type *old_last = exec->vtx.vertex + old_vtx_size_no_pos - 1;
311 fi_type *new_last = exec->vtx.vertex + exec->vtx.vertex_size_no_pos - 1;
312
313 if (size_diff < 0) {
314 /* Decreasing the size: Copy from first to last to move
315 * elements to the left.
316 */
317 fi_type *old_end = old_last + 1;
318 fi_type *old = old_first;
319 fi_type *new = new_first;
320
321 do {
322 *new++ = *old++;
323 } while (old != old_end);
324 } else {
325 /* Increasing the size: Copy from last to first to move
326 * elements to the right.
327 */
328 fi_type *old_end = old_first - 1;
329 fi_type *old = old_last;
330 fi_type *new = new_last;
331
332 do {
333 *new-- = *old--;
334 } while (old != old_end);
335 }
336
337 /* Update pointers to attribs, because we moved them. */
338 GLbitfield64 enabled = exec->vtx.enabled &
339 ~BITFIELD64_BIT(VBO_ATTRIB_POS) &
340 ~BITFIELD64_BIT(attr);
341 while (enabled) {
342 unsigned i = u_bit_scan64(&enabled);
343
344 if (exec->vtx.attrptr[i] > exec->vtx.attrptr[attr])
345 exec->vtx.attrptr[i] += size_diff;
346 }
347 }
348 } else {
349 /* Just have to append the new attribute at the end */
350 exec->vtx.attrptr[attr] = exec->vtx.vertex +
351 exec->vtx.vertex_size_no_pos - newSize;
352 }
353 }
354
355 /* The position is always last. */
356 exec->vtx.attrptr[0] = exec->vtx.vertex + exec->vtx.vertex_size_no_pos;
357
358 /* Replay stored vertices to translate them
359 * to new format here.
360 *
361 * -- No need to replay - just copy piecewise
362 */
363 if (unlikely(exec->vtx.copied.nr)) {
364 fi_type *data = exec->vtx.copied.buffer;
365 fi_type *dest = exec->vtx.buffer_ptr;
366
367 assert(exec->vtx.buffer_ptr == exec->vtx.buffer_map);
368
369 for (i = 0 ; i < exec->vtx.copied.nr ; i++) {
370 GLbitfield64 enabled = exec->vtx.enabled;
371 while (enabled) {
372 const int j = u_bit_scan64(&enabled);
373 GLuint sz = exec->vtx.attr[j].size;
374 GLint old_offset = old_attrptr[j] - exec->vtx.vertex;
375 GLint new_offset = exec->vtx.attrptr[j] - exec->vtx.vertex;
376
377 assert(sz);
378
379 if (j == attr) {
380 if (oldSize) {
381 fi_type tmp[4];
382 COPY_CLEAN_4V_TYPE_AS_UNION(tmp, oldSize,
383 data + old_offset,
384 exec->vtx.attr[j].type);
385 COPY_SZ_4V(dest + new_offset, newSize, tmp);
386 } else {
387 fi_type *current = (fi_type *)vbo->current[j].Ptr;
388 COPY_SZ_4V(dest + new_offset, sz, current);
389 }
390 }
391 else {
392 COPY_SZ_4V(dest + new_offset, sz, data + old_offset);
393 }
394 }
395
396 data += old_vtx_size;
397 dest += exec->vtx.vertex_size;
398 }
399
400 exec->vtx.buffer_ptr = dest;
401 exec->vtx.vert_count += exec->vtx.copied.nr;
402 exec->vtx.copied.nr = 0;
403 }
404 }
405
406
407 /**
408 * This is when a vertex attribute transitions to a different size.
409 * For example, we saw a bunch of glTexCoord2f() calls and now we got a
410 * glTexCoord4f() call. We promote the array from size=2 to size=4.
411 * \param newSize size of new vertex (number of 32-bit words).
412 * \param attr VBO_ATTRIB_x vertex attribute value
413 */
414 static void
vbo_exec_fixup_vertex(struct gl_context * ctx,GLuint attr,GLuint newSize,GLenum newType)415 vbo_exec_fixup_vertex(struct gl_context *ctx, GLuint attr,
416 GLuint newSize, GLenum newType)
417 {
418 struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
419
420 assert(attr < VBO_ATTRIB_MAX);
421
422 if (newSize > exec->vtx.attr[attr].size ||
423 newType != exec->vtx.attr[attr].type) {
424 /* New size is larger. Need to flush existing vertices and get
425 * an enlarged vertex format.
426 */
427 vbo_exec_wrap_upgrade_vertex(exec, attr, newSize, newType);
428 }
429 else if (newSize < exec->vtx.attr[attr].active_size) {
430 GLuint i;
431 const fi_type *id =
432 vbo_get_default_vals_as_union(exec->vtx.attr[attr].type);
433
434 /* New size is smaller - just need to fill in some
435 * zeros. Don't need to flush or wrap.
436 */
437 for (i = newSize; i <= exec->vtx.attr[attr].size; i++)
438 exec->vtx.attrptr[attr][i-1] = id[i-1];
439
440 exec->vtx.attr[attr].active_size = newSize;
441 }
442 }
443
444
445 /**
446 * If index=0, does glVertexAttrib*() alias glVertex() to emit a vertex?
447 * It depends on a few things, including whether we're inside or outside
448 * of glBegin/glEnd.
449 */
450 static inline bool
is_vertex_position(const struct gl_context * ctx,GLuint index)451 is_vertex_position(const struct gl_context *ctx, GLuint index)
452 {
453 return (index == 0 &&
454 _mesa_attr_zero_aliases_vertex(ctx) &&
455 _mesa_inside_begin_end(ctx));
456 }
457
458 /* Write a 64-bit value into a 32-bit pointer by preserving endianness. */
459 #if UTIL_ARCH_LITTLE_ENDIAN
460 #define SET_64BIT(dst32, u64) do { \
461 *(dst32)++ = (u64); \
462 *(dst32)++ = (uint64_t)(u64) >> 32; \
463 } while (0)
464 #else
465 #define SET_64BIT(dst32, u64) do { \
466 *(dst32)++ = (uint64_t)(u64) >> 32; \
467 *(dst32)++ = (u64); \
468 } while (0)
469 #endif
470
471
472 /**
473 * This macro is used to implement all the glVertex, glColor, glTexCoord,
474 * glVertexAttrib, etc functions.
475 * \param A VBO_ATTRIB_x attribute index
476 * \param N attribute size (1..4)
477 * \param T type (GL_FLOAT, GL_DOUBLE, GL_INT, GL_UNSIGNED_INT)
478 * \param C cast type (uint32_t or uint64_t)
479 * \param V0, V1, v2, V3 attribute value
480 */
481 #define ATTR_UNION(A, N, T, C, V0, V1, V2, V3) \
482 do { \
483 struct vbo_exec_context *exec = &vbo_context(ctx)->exec; \
484 int sz = (sizeof(C) / sizeof(GLfloat)); \
485 \
486 assert(sz == 1 || sz == 2); \
487 \
488 /* store a copy of the attribute in exec except for glVertex */ \
489 if ((A) != 0) { \
490 /* Check if attribute size or type is changing. */ \
491 if (unlikely(exec->vtx.attr[A].active_size != N * sz || \
492 exec->vtx.attr[A].type != T)) { \
493 vbo_exec_fixup_vertex(ctx, A, N * sz, T); \
494 } \
495 \
496 C *dest = (C *)exec->vtx.attrptr[A]; \
497 if (N>0) dest[0] = V0; \
498 if (N>1) dest[1] = V1; \
499 if (N>2) dest[2] = V2; \
500 if (N>3) dest[3] = V3; \
501 assert(exec->vtx.attr[A].type == T); \
502 \
503 /* we now have accumulated a per-vertex attribute */ \
504 ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT; \
505 } else { \
506 /* This is a glVertex call */ \
507 int size = exec->vtx.attr[0].size; \
508 \
509 /* Check if attribute size or type is changing. */ \
510 if (unlikely(size < N * sz || \
511 exec->vtx.attr[0].type != T)) { \
512 vbo_exec_wrap_upgrade_vertex(exec, 0, N * sz, T); \
513 } \
514 \
515 uint32_t *dst = (uint32_t *)exec->vtx.buffer_ptr; \
516 uint32_t *src = (uint32_t *)exec->vtx.vertex; \
517 unsigned vertex_size_no_pos = exec->vtx.vertex_size_no_pos; \
518 \
519 /* Copy over attributes from exec. */ \
520 for (unsigned i = 0; i < vertex_size_no_pos; i++) \
521 *dst++ = *src++; \
522 \
523 /* Store the position, which is always last and can have 32 or */ \
524 /* 64 bits per channel. */ \
525 if (sizeof(C) == 4) { \
526 if (N > 0) *dst++ = V0; \
527 if (N > 1) *dst++ = V1; \
528 if (N > 2) *dst++ = V2; \
529 if (N > 3) *dst++ = V3; \
530 \
531 if (unlikely(N < size)) { \
532 if (N < 2 && size >= 2) *dst++ = V1; \
533 if (N < 3 && size >= 3) *dst++ = V2; \
534 if (N < 4 && size >= 4) *dst++ = V3; \
535 } \
536 } else { \
537 /* 64 bits: dst can be unaligned, so copy each 4-byte word */ \
538 /* separately */ \
539 if (N > 0) SET_64BIT(dst, V0); \
540 if (N > 1) SET_64BIT(dst, V1); \
541 if (N > 2) SET_64BIT(dst, V2); \
542 if (N > 3) SET_64BIT(dst, V3); \
543 \
544 if (unlikely(N * 2 < size)) { \
545 if (N < 2 && size >= 4) SET_64BIT(dst, V1); \
546 if (N < 3 && size >= 6) SET_64BIT(dst, V2); \
547 if (N < 4 && size >= 8) SET_64BIT(dst, V3); \
548 } \
549 } \
550 \
551 /* dst now points at the beginning of the next vertex */ \
552 exec->vtx.buffer_ptr = (fi_type*)dst; \
553 \
554 /* Don't set FLUSH_UPDATE_CURRENT because */ \
555 /* Current.Attrib[VBO_ATTRIB_POS] is never used. */ \
556 \
557 if (unlikely(++exec->vtx.vert_count >= exec->vtx.max_vert)) \
558 vbo_exec_vtx_wrap(exec); \
559 } \
560 } while (0)
561
562
563 #undef ERROR
564 #define ERROR(err) _mesa_error(ctx, err, __func__)
565 #define TAG(x) vbo_exec_##x
566
567 #include "vbo_attrib_tmp.h"
568
569
570
571 /**
572 * Execute a glMaterial call. Note that if GL_COLOR_MATERIAL is enabled,
573 * this may be a (partial) no-op.
574 */
575 static void GLAPIENTRY
vbo_exec_Materialfv(GLenum face,GLenum pname,const GLfloat * params)576 vbo_exec_Materialfv(GLenum face, GLenum pname, const GLfloat *params)
577 {
578 GLbitfield updateMats;
579 GET_CURRENT_CONTEXT(ctx);
580
581 /* This function should be a no-op when it tries to update material
582 * attributes which are currently tracking glColor via glColorMaterial.
583 * The updateMats var will be a mask of the MAT_BIT_FRONT/BACK_x bits
584 * indicating which material attributes can actually be updated below.
585 */
586 if (ctx->Light.ColorMaterialEnabled) {
587 updateMats = ~ctx->Light._ColorMaterialBitmask;
588 }
589 else {
590 /* GL_COLOR_MATERIAL is disabled so don't skip any material updates */
591 updateMats = ALL_MATERIAL_BITS;
592 }
593
594 if (ctx->API == API_OPENGL_COMPAT && face == GL_FRONT) {
595 updateMats &= FRONT_MATERIAL_BITS;
596 }
597 else if (ctx->API == API_OPENGL_COMPAT && face == GL_BACK) {
598 updateMats &= BACK_MATERIAL_BITS;
599 }
600 else if (face != GL_FRONT_AND_BACK) {
601 _mesa_error(ctx, GL_INVALID_ENUM, "glMaterial(invalid face)");
602 return;
603 }
604
605 switch (pname) {
606 case GL_EMISSION:
607 if (updateMats & MAT_BIT_FRONT_EMISSION)
608 MAT_ATTR(VBO_ATTRIB_MAT_FRONT_EMISSION, 4, params);
609 if (updateMats & MAT_BIT_BACK_EMISSION)
610 MAT_ATTR(VBO_ATTRIB_MAT_BACK_EMISSION, 4, params);
611 break;
612 case GL_AMBIENT:
613 if (updateMats & MAT_BIT_FRONT_AMBIENT)
614 MAT_ATTR(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, params);
615 if (updateMats & MAT_BIT_BACK_AMBIENT)
616 MAT_ATTR(VBO_ATTRIB_MAT_BACK_AMBIENT, 4, params);
617 break;
618 case GL_DIFFUSE:
619 if (updateMats & MAT_BIT_FRONT_DIFFUSE)
620 MAT_ATTR(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, params);
621 if (updateMats & MAT_BIT_BACK_DIFFUSE)
622 MAT_ATTR(VBO_ATTRIB_MAT_BACK_DIFFUSE, 4, params);
623 break;
624 case GL_SPECULAR:
625 if (updateMats & MAT_BIT_FRONT_SPECULAR)
626 MAT_ATTR(VBO_ATTRIB_MAT_FRONT_SPECULAR, 4, params);
627 if (updateMats & MAT_BIT_BACK_SPECULAR)
628 MAT_ATTR(VBO_ATTRIB_MAT_BACK_SPECULAR, 4, params);
629 break;
630 case GL_SHININESS:
631 if (*params < 0 || *params > ctx->Const.MaxShininess) {
632 _mesa_error(ctx, GL_INVALID_VALUE,
633 "glMaterial(invalid shininess: %f out range [0, %f])",
634 *params, ctx->Const.MaxShininess);
635 return;
636 }
637 if (updateMats & MAT_BIT_FRONT_SHININESS)
638 MAT_ATTR(VBO_ATTRIB_MAT_FRONT_SHININESS, 1, params);
639 if (updateMats & MAT_BIT_BACK_SHININESS)
640 MAT_ATTR(VBO_ATTRIB_MAT_BACK_SHININESS, 1, params);
641 break;
642 case GL_COLOR_INDEXES:
643 if (ctx->API != API_OPENGL_COMPAT) {
644 _mesa_error(ctx, GL_INVALID_ENUM, "glMaterialfv(pname)");
645 return;
646 }
647 if (updateMats & MAT_BIT_FRONT_INDEXES)
648 MAT_ATTR(VBO_ATTRIB_MAT_FRONT_INDEXES, 3, params);
649 if (updateMats & MAT_BIT_BACK_INDEXES)
650 MAT_ATTR(VBO_ATTRIB_MAT_BACK_INDEXES, 3, params);
651 break;
652 case GL_AMBIENT_AND_DIFFUSE:
653 if (updateMats & MAT_BIT_FRONT_AMBIENT)
654 MAT_ATTR(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, params);
655 if (updateMats & MAT_BIT_FRONT_DIFFUSE)
656 MAT_ATTR(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, params);
657 if (updateMats & MAT_BIT_BACK_AMBIENT)
658 MAT_ATTR(VBO_ATTRIB_MAT_BACK_AMBIENT, 4, params);
659 if (updateMats & MAT_BIT_BACK_DIFFUSE)
660 MAT_ATTR(VBO_ATTRIB_MAT_BACK_DIFFUSE, 4, params);
661 break;
662 default:
663 _mesa_error(ctx, GL_INVALID_ENUM, "glMaterialfv(pname)");
664 return;
665 }
666 }
667
668
669 /**
670 * Flush (draw) vertices.
671 *
672 * \param flags bitmask of FLUSH_STORED_VERTICES, FLUSH_UPDATE_CURRENT
673 */
674 static void
vbo_exec_FlushVertices_internal(struct vbo_exec_context * exec,unsigned flags)675 vbo_exec_FlushVertices_internal(struct vbo_exec_context *exec, unsigned flags)
676 {
677 struct gl_context *ctx = exec->ctx;
678
679 if (flags & FLUSH_STORED_VERTICES) {
680 if (exec->vtx.vert_count) {
681 vbo_exec_vtx_flush(exec);
682 }
683
684 if (exec->vtx.vertex_size) {
685 vbo_exec_copy_to_current(exec);
686 vbo_reset_all_attr(exec);
687 }
688
689 /* All done. */
690 ctx->Driver.NeedFlush = 0;
691 } else {
692 assert(flags == FLUSH_UPDATE_CURRENT);
693
694 /* Note that the vertex size is unchanged.
695 * (vbo_reset_all_attr isn't called)
696 */
697 vbo_exec_copy_to_current(exec);
698
699 /* Only FLUSH_UPDATE_CURRENT is done. */
700 ctx->Driver.NeedFlush = ~FLUSH_UPDATE_CURRENT;
701 }
702 }
703
704
705 static void GLAPIENTRY
vbo_exec_EvalCoord1f(GLfloat u)706 vbo_exec_EvalCoord1f(GLfloat u)
707 {
708 GET_CURRENT_CONTEXT(ctx);
709 struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
710
711 {
712 GLint i;
713 if (exec->eval.recalculate_maps)
714 vbo_exec_eval_update(exec);
715
716 for (i = 0; i <= VBO_ATTRIB_TEX7; i++) {
717 if (exec->eval.map1[i].map)
718 if (exec->vtx.attr[i].active_size != exec->eval.map1[i].sz)
719 vbo_exec_fixup_vertex(ctx, i, exec->eval.map1[i].sz, GL_FLOAT);
720 }
721 }
722
723 memcpy(exec->vtx.copied.buffer, exec->vtx.vertex,
724 exec->vtx.vertex_size * sizeof(GLfloat));
725
726 vbo_exec_do_EvalCoord1f(exec, u);
727
728 memcpy(exec->vtx.vertex, exec->vtx.copied.buffer,
729 exec->vtx.vertex_size * sizeof(GLfloat));
730 }
731
732
733 static void GLAPIENTRY
vbo_exec_EvalCoord2f(GLfloat u,GLfloat v)734 vbo_exec_EvalCoord2f(GLfloat u, GLfloat v)
735 {
736 GET_CURRENT_CONTEXT(ctx);
737 struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
738
739 {
740 GLint i;
741 if (exec->eval.recalculate_maps)
742 vbo_exec_eval_update(exec);
743
744 for (i = 0; i <= VBO_ATTRIB_TEX7; i++) {
745 if (exec->eval.map2[i].map)
746 if (exec->vtx.attr[i].active_size != exec->eval.map2[i].sz)
747 vbo_exec_fixup_vertex(ctx, i, exec->eval.map2[i].sz, GL_FLOAT);
748 }
749
750 if (ctx->Eval.AutoNormal)
751 if (exec->vtx.attr[VBO_ATTRIB_NORMAL].active_size != 3)
752 vbo_exec_fixup_vertex(ctx, VBO_ATTRIB_NORMAL, 3, GL_FLOAT);
753 }
754
755 memcpy(exec->vtx.copied.buffer, exec->vtx.vertex,
756 exec->vtx.vertex_size * sizeof(GLfloat));
757
758 vbo_exec_do_EvalCoord2f(exec, u, v);
759
760 memcpy(exec->vtx.vertex, exec->vtx.copied.buffer,
761 exec->vtx.vertex_size * sizeof(GLfloat));
762 }
763
764
765 static void GLAPIENTRY
vbo_exec_EvalCoord1fv(const GLfloat * u)766 vbo_exec_EvalCoord1fv(const GLfloat *u)
767 {
768 vbo_exec_EvalCoord1f(u[0]);
769 }
770
771
772 static void GLAPIENTRY
vbo_exec_EvalCoord2fv(const GLfloat * u)773 vbo_exec_EvalCoord2fv(const GLfloat *u)
774 {
775 vbo_exec_EvalCoord2f(u[0], u[1]);
776 }
777
778
779 static void GLAPIENTRY
vbo_exec_EvalPoint1(GLint i)780 vbo_exec_EvalPoint1(GLint i)
781 {
782 GET_CURRENT_CONTEXT(ctx);
783 GLfloat du = ((ctx->Eval.MapGrid1u2 - ctx->Eval.MapGrid1u1) /
784 (GLfloat) ctx->Eval.MapGrid1un);
785 GLfloat u = i * du + ctx->Eval.MapGrid1u1;
786
787 vbo_exec_EvalCoord1f(u);
788 }
789
790
791 static void GLAPIENTRY
vbo_exec_EvalPoint2(GLint i,GLint j)792 vbo_exec_EvalPoint2(GLint i, GLint j)
793 {
794 GET_CURRENT_CONTEXT(ctx);
795 GLfloat du = ((ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1) /
796 (GLfloat) ctx->Eval.MapGrid2un);
797 GLfloat dv = ((ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1) /
798 (GLfloat) ctx->Eval.MapGrid2vn);
799 GLfloat u = i * du + ctx->Eval.MapGrid2u1;
800 GLfloat v = j * dv + ctx->Eval.MapGrid2v1;
801
802 vbo_exec_EvalCoord2f(u, v);
803 }
804
805
806 /**
807 * Called via glBegin.
808 */
809 static void GLAPIENTRY
vbo_exec_Begin(GLenum mode)810 vbo_exec_Begin(GLenum mode)
811 {
812 GET_CURRENT_CONTEXT(ctx);
813 struct vbo_context *vbo = vbo_context(ctx);
814 struct vbo_exec_context *exec = &vbo->exec;
815 int i;
816
817 if (_mesa_inside_begin_end(ctx)) {
818 _mesa_error(ctx, GL_INVALID_OPERATION, "glBegin");
819 return;
820 }
821
822 if (!_mesa_valid_prim_mode(ctx, mode, "glBegin")) {
823 return;
824 }
825
826 if (!_mesa_valid_to_render(ctx, "glBegin")) {
827 return;
828 }
829
830 /* Heuristic: attempt to isolate attributes occurring outside
831 * begin/end pairs.
832 *
833 * Use FLUSH_STORED_VERTICES, because it updates current attribs and
834 * sets vertex_size to 0. (FLUSH_UPDATE_CURRENT doesn't change vertex_size)
835 */
836 if (exec->vtx.vertex_size && !exec->vtx.attr[VBO_ATTRIB_POS].size)
837 vbo_exec_FlushVertices_internal(exec, FLUSH_STORED_VERTICES);
838
839 i = exec->vtx.prim_count++;
840 exec->vtx.prim[i].mode = mode;
841 exec->vtx.prim[i].begin = 1;
842 exec->vtx.prim[i].end = 0;
843 exec->vtx.prim[i].start = exec->vtx.vert_count;
844 exec->vtx.prim[i].count = 0;
845
846 ctx->Driver.CurrentExecPrimitive = mode;
847
848 ctx->Exec = ctx->BeginEnd;
849
850 /* We may have been called from a display list, in which case we should
851 * leave dlist.c's dispatch table in place.
852 */
853 if (ctx->CurrentClientDispatch == ctx->MarshalExec) {
854 ctx->CurrentServerDispatch = ctx->Exec;
855 } else if (ctx->CurrentClientDispatch == ctx->OutsideBeginEnd) {
856 ctx->CurrentClientDispatch = ctx->Exec;
857 _glapi_set_dispatch(ctx->CurrentClientDispatch);
858 } else {
859 assert(ctx->CurrentClientDispatch == ctx->Save);
860 }
861 }
862
863
864 /**
865 * Try to merge / concatenate the two most recent VBO primitives.
866 */
867 static void
try_vbo_merge(struct vbo_exec_context * exec)868 try_vbo_merge(struct vbo_exec_context *exec)
869 {
870 struct _mesa_prim *cur = &exec->vtx.prim[exec->vtx.prim_count - 1];
871
872 assert(exec->vtx.prim_count >= 1);
873
874 vbo_try_prim_conversion(cur);
875
876 if (exec->vtx.prim_count >= 2) {
877 struct _mesa_prim *prev = &exec->vtx.prim[exec->vtx.prim_count - 2];
878 assert(prev == cur - 1);
879
880 if (vbo_merge_draws(exec->ctx, false, prev, cur))
881 exec->vtx.prim_count--; /* drop the last primitive */
882 }
883 }
884
885
886 /**
887 * Called via glEnd.
888 */
889 static void GLAPIENTRY
vbo_exec_End(void)890 vbo_exec_End(void)
891 {
892 GET_CURRENT_CONTEXT(ctx);
893 struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
894
895 if (!_mesa_inside_begin_end(ctx)) {
896 _mesa_error(ctx, GL_INVALID_OPERATION, "glEnd");
897 return;
898 }
899
900 ctx->Exec = ctx->OutsideBeginEnd;
901
902 if (ctx->CurrentClientDispatch == ctx->MarshalExec) {
903 ctx->CurrentServerDispatch = ctx->Exec;
904 } else if (ctx->CurrentClientDispatch == ctx->BeginEnd) {
905 ctx->CurrentClientDispatch = ctx->Exec;
906 _glapi_set_dispatch(ctx->CurrentClientDispatch);
907 }
908
909 if (exec->vtx.prim_count > 0) {
910 /* close off current primitive */
911 struct _mesa_prim *last_prim = &exec->vtx.prim[exec->vtx.prim_count - 1];
912 unsigned count = exec->vtx.vert_count - last_prim->start;
913
914 last_prim->end = 1;
915 last_prim->count = count;
916
917 if (count)
918 ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES;
919
920 /* Special handling for GL_LINE_LOOP */
921 if (last_prim->mode == GL_LINE_LOOP && last_prim->begin == 0) {
922 /* We're finishing drawing a line loop. Append 0th vertex onto
923 * end of vertex buffer so we can draw it as a line strip.
924 */
925 const fi_type *src = exec->vtx.buffer_map +
926 last_prim->start * exec->vtx.vertex_size;
927 fi_type *dst = exec->vtx.buffer_map +
928 exec->vtx.vert_count * exec->vtx.vertex_size;
929
930 /* copy 0th vertex to end of buffer */
931 memcpy(dst, src, exec->vtx.vertex_size * sizeof(fi_type));
932
933 last_prim->start++; /* skip vertex0 */
934 /* note that last_prim->count stays unchanged */
935 last_prim->mode = GL_LINE_STRIP;
936
937 /* Increment the vertex count so the next primitive doesn't
938 * overwrite the last vertex which we just added.
939 */
940 exec->vtx.vert_count++;
941 exec->vtx.buffer_ptr += exec->vtx.vertex_size;
942 }
943
944 try_vbo_merge(exec);
945 }
946
947 ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END;
948
949 if (exec->vtx.prim_count == VBO_MAX_PRIM)
950 vbo_exec_vtx_flush(exec);
951
952 if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) {
953 _mesa_flush(ctx);
954 }
955 }
956
957
958 /**
959 * Called via glPrimitiveRestartNV()
960 */
961 static void GLAPIENTRY
vbo_exec_PrimitiveRestartNV(void)962 vbo_exec_PrimitiveRestartNV(void)
963 {
964 GLenum curPrim;
965 GET_CURRENT_CONTEXT(ctx);
966
967 curPrim = ctx->Driver.CurrentExecPrimitive;
968
969 if (curPrim == PRIM_OUTSIDE_BEGIN_END) {
970 _mesa_error(ctx, GL_INVALID_OPERATION, "glPrimitiveRestartNV");
971 }
972 else {
973 vbo_exec_End();
974 vbo_exec_Begin(curPrim);
975 }
976 }
977
978
979 static void
vbo_exec_vtxfmt_init(struct vbo_exec_context * exec)980 vbo_exec_vtxfmt_init(struct vbo_exec_context *exec)
981 {
982 struct gl_context *ctx = exec->ctx;
983 GLvertexformat *vfmt = &exec->vtxfmt;
984
985 #define NAME_AE(x) _ae_##x
986 #define NAME_CALLLIST(x) _mesa_##x
987 #define NAME(x) vbo_exec_##x
988 #define NAME_ES(x) _es_##x
989
990 #include "vbo_init_tmp.h"
991 }
992
993
994 static void
vbo_reset_all_attr(struct vbo_exec_context * exec)995 vbo_reset_all_attr(struct vbo_exec_context *exec)
996 {
997 while (exec->vtx.enabled) {
998 const int i = u_bit_scan64(&exec->vtx.enabled);
999
1000 /* Reset the vertex attribute by setting its size to zero. */
1001 exec->vtx.attr[i].size = 0;
1002 exec->vtx.attr[i].type = GL_FLOAT;
1003 exec->vtx.attr[i].active_size = 0;
1004 exec->vtx.attrptr[i] = NULL;
1005 }
1006
1007 exec->vtx.vertex_size = 0;
1008 }
1009
1010
1011 void
vbo_exec_vtx_init(struct vbo_exec_context * exec,bool use_buffer_objects)1012 vbo_exec_vtx_init(struct vbo_exec_context *exec, bool use_buffer_objects)
1013 {
1014 struct gl_context *ctx = exec->ctx;
1015
1016 if (use_buffer_objects) {
1017 /* Use buffer objects for immediate mode. */
1018 struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
1019
1020 exec->vtx.bufferobj = ctx->Driver.NewBufferObject(ctx, IMM_BUFFER_NAME);
1021
1022 /* Map the buffer. */
1023 vbo_exec_vtx_map(exec);
1024 assert(exec->vtx.buffer_ptr);
1025 } else {
1026 /* Use allocated memory for immediate mode. */
1027 exec->vtx.bufferobj = NULL;
1028 exec->vtx.buffer_map =
1029 align_malloc(ctx->Const.glBeginEndBufferSize, 64);
1030 exec->vtx.buffer_ptr = exec->vtx.buffer_map;
1031 }
1032
1033 vbo_exec_vtxfmt_init(exec);
1034 _mesa_noop_vtxfmt_init(ctx, &exec->vtxfmt_noop);
1035
1036 exec->vtx.enabled = u_bit_consecutive64(0, VBO_ATTRIB_MAX); /* reset all */
1037 vbo_reset_all_attr(exec);
1038 }
1039
1040
1041 void
vbo_exec_vtx_destroy(struct vbo_exec_context * exec)1042 vbo_exec_vtx_destroy(struct vbo_exec_context *exec)
1043 {
1044 /* using a real VBO for vertex data */
1045 struct gl_context *ctx = exec->ctx;
1046
1047 /* True VBOs should already be unmapped
1048 */
1049 if (exec->vtx.buffer_map) {
1050 assert(!exec->vtx.bufferobj ||
1051 exec->vtx.bufferobj->Name == IMM_BUFFER_NAME);
1052 if (!exec->vtx.bufferobj) {
1053 align_free(exec->vtx.buffer_map);
1054 exec->vtx.buffer_map = NULL;
1055 exec->vtx.buffer_ptr = NULL;
1056 }
1057 }
1058
1059 /* Free the vertex buffer. Unmap first if needed.
1060 */
1061 if (exec->vtx.bufferobj &&
1062 _mesa_bufferobj_mapped(exec->vtx.bufferobj, MAP_INTERNAL)) {
1063 ctx->Driver.UnmapBuffer(ctx, exec->vtx.bufferobj, MAP_INTERNAL);
1064 }
1065 _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL);
1066 }
1067
1068
1069 /**
1070 * If inside glBegin()/glEnd(), it should assert(0). Otherwise, if
1071 * FLUSH_STORED_VERTICES bit in \p flags is set flushes any buffered
1072 * vertices, if FLUSH_UPDATE_CURRENT bit is set updates
1073 * __struct gl_contextRec::Current and gl_light_attrib::Material
1074 *
1075 * Note that the default T&L engine never clears the
1076 * FLUSH_UPDATE_CURRENT bit, even after performing the update.
1077 *
1078 * \param flags bitmask of FLUSH_STORED_VERTICES, FLUSH_UPDATE_CURRENT
1079 */
1080 void
vbo_exec_FlushVertices(struct gl_context * ctx,GLuint flags)1081 vbo_exec_FlushVertices(struct gl_context *ctx, GLuint flags)
1082 {
1083 struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
1084
1085 #ifndef NDEBUG
1086 /* debug check: make sure we don't get called recursively */
1087 exec->flush_call_depth++;
1088 assert(exec->flush_call_depth == 1);
1089 #endif
1090
1091 if (_mesa_inside_begin_end(ctx)) {
1092 /* We've had glBegin but not glEnd! */
1093 #ifndef NDEBUG
1094 exec->flush_call_depth--;
1095 assert(exec->flush_call_depth == 0);
1096 #endif
1097 return;
1098 }
1099
1100 /* Flush (draw). */
1101 vbo_exec_FlushVertices_internal(exec, flags);
1102
1103 #ifndef NDEBUG
1104 exec->flush_call_depth--;
1105 assert(exec->flush_call_depth == 0);
1106 #endif
1107 }
1108
1109
1110 void GLAPIENTRY
_es_Color4f(GLfloat r,GLfloat g,GLfloat b,GLfloat a)1111 _es_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
1112 {
1113 vbo_exec_Color4f(r, g, b, a);
1114 }
1115
1116
1117 void GLAPIENTRY
_es_Normal3f(GLfloat x,GLfloat y,GLfloat z)1118 _es_Normal3f(GLfloat x, GLfloat y, GLfloat z)
1119 {
1120 vbo_exec_Normal3f(x, y, z);
1121 }
1122
1123
1124 void GLAPIENTRY
_es_MultiTexCoord4f(GLenum target,GLfloat s,GLfloat t,GLfloat r,GLfloat q)1125 _es_MultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
1126 {
1127 vbo_exec_MultiTexCoord4f(target, s, t, r, q);
1128 }
1129
1130
1131 void GLAPIENTRY
_es_Materialfv(GLenum face,GLenum pname,const GLfloat * params)1132 _es_Materialfv(GLenum face, GLenum pname, const GLfloat *params)
1133 {
1134 vbo_exec_Materialfv(face, pname, params);
1135 }
1136
1137
1138 void GLAPIENTRY
_es_Materialf(GLenum face,GLenum pname,GLfloat param)1139 _es_Materialf(GLenum face, GLenum pname, GLfloat param)
1140 {
1141 GLfloat p[4];
1142 p[0] = param;
1143 p[1] = p[2] = p[3] = 0.0F;
1144 vbo_exec_Materialfv(face, pname, p);
1145 }
1146
1147
1148 /**
1149 * A special version of glVertexAttrib4f that does not treat index 0 as
1150 * VBO_ATTRIB_POS.
1151 */
1152 static void
VertexAttrib4f_nopos(GLuint index,GLfloat x,GLfloat y,GLfloat z,GLfloat w)1153 VertexAttrib4f_nopos(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
1154 {
1155 GET_CURRENT_CONTEXT(ctx);
1156 if (index < MAX_VERTEX_GENERIC_ATTRIBS)
1157 ATTRF(VBO_ATTRIB_GENERIC0 + index, 4, x, y, z, w);
1158 else
1159 ERROR(GL_INVALID_VALUE);
1160 }
1161
1162 void GLAPIENTRY
_es_VertexAttrib4f(GLuint index,GLfloat x,GLfloat y,GLfloat z,GLfloat w)1163 _es_VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
1164 {
1165 VertexAttrib4f_nopos(index, x, y, z, w);
1166 }
1167
1168
1169 void GLAPIENTRY
_es_VertexAttrib1f(GLuint indx,GLfloat x)1170 _es_VertexAttrib1f(GLuint indx, GLfloat x)
1171 {
1172 VertexAttrib4f_nopos(indx, x, 0.0f, 0.0f, 1.0f);
1173 }
1174
1175
1176 void GLAPIENTRY
_es_VertexAttrib1fv(GLuint indx,const GLfloat * values)1177 _es_VertexAttrib1fv(GLuint indx, const GLfloat* values)
1178 {
1179 VertexAttrib4f_nopos(indx, values[0], 0.0f, 0.0f, 1.0f);
1180 }
1181
1182
1183 void GLAPIENTRY
_es_VertexAttrib2f(GLuint indx,GLfloat x,GLfloat y)1184 _es_VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y)
1185 {
1186 VertexAttrib4f_nopos(indx, x, y, 0.0f, 1.0f);
1187 }
1188
1189
1190 void GLAPIENTRY
_es_VertexAttrib2fv(GLuint indx,const GLfloat * values)1191 _es_VertexAttrib2fv(GLuint indx, const GLfloat* values)
1192 {
1193 VertexAttrib4f_nopos(indx, values[0], values[1], 0.0f, 1.0f);
1194 }
1195
1196
1197 void GLAPIENTRY
_es_VertexAttrib3f(GLuint indx,GLfloat x,GLfloat y,GLfloat z)1198 _es_VertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z)
1199 {
1200 VertexAttrib4f_nopos(indx, x, y, z, 1.0f);
1201 }
1202
1203
1204 void GLAPIENTRY
_es_VertexAttrib3fv(GLuint indx,const GLfloat * values)1205 _es_VertexAttrib3fv(GLuint indx, const GLfloat* values)
1206 {
1207 VertexAttrib4f_nopos(indx, values[0], values[1], values[2], 1.0f);
1208 }
1209
1210
1211 void GLAPIENTRY
_es_VertexAttrib4fv(GLuint indx,const GLfloat * values)1212 _es_VertexAttrib4fv(GLuint indx, const GLfloat* values)
1213 {
1214 VertexAttrib4f_nopos(indx, values[0], values[1], values[2], values[3]);
1215 }
1216