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