vbo: Test for VBO_SAVE_PRIM_WEAK in _mesa_prim::mode is false.
[mesa.git] / src / mesa / vbo / vbo_save_api.c
index 8eb38464781920866f68ccb7510a6a5e593e826d..4cc315136c7fc35f764ca84b502e5078464ea885 100644 (file)
@@ -68,27 +68,34 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
 #include "main/glheader.h"
+#include "main/arrayobj.h"
 #include "main/bufferobj.h"
 #include "main/context.h"
 #include "main/dlist.h"
 #include "main/enums.h"
 #include "main/eval.h"
 #include "main/macros.h"
-#include "main/api_validate.h"
+#include "main/draw_validate.h"
 #include "main/api_arrayelt.h"
 #include "main/vtxfmt.h"
 #include "main/dispatch.h"
 #include "main/state.h"
+#include "main/varray.h"
 #include "util/bitscan.h"
 
-#include "vbo_context.h"
 #include "vbo_noop.h"
+#include "vbo_private.h"
 
 
 #ifdef ERROR
 #undef ERROR
 #endif
 
+/**
+ * Display list flag only used by this VBO code.
+ */
+#define DLIST_DANGLING_REFS     0x1
+
 
 /* An interesting VBO number/name to help with debugging */
 #define VBO_BUF_ID  12345
@@ -174,7 +181,7 @@ copy_vertices(struct gl_context *ctx,
                 sz * sizeof(GLfloat));
       return i;
    default:
-      assert(0);
+      unreachable("Unexpected primitive type");
       return 0;
    }
 }
@@ -215,7 +222,6 @@ alloc_vertex_store(struct gl_context *ctx)
 
    vertex_store->buffer_map = NULL;
    vertex_store->used = 0;
-   vertex_store->refcount = 1;
 
    return vertex_store;
 }
@@ -406,6 +412,119 @@ convert_line_loop_to_strip(struct vbo_save_context *save,
 }
 
 
+/* Compare the present vao if it has the same setup. */
+static bool
+compare_vao(gl_vertex_processing_mode mode,
+            const struct gl_vertex_array_object *vao,
+            const struct gl_buffer_object *bo, GLintptr buffer_offset,
+            GLuint stride, GLbitfield64 vao_enabled,
+            const GLubyte size[VBO_ATTRIB_MAX],
+            const GLenum16 type[VBO_ATTRIB_MAX],
+            const GLuint offset[VBO_ATTRIB_MAX])
+{
+   if (!vao)
+      return false;
+
+   /* If the enabled arrays are not the same we are not equal. */
+   if (vao_enabled != vao->_Enabled)
+      return false;
+
+   /* Check the buffer binding at 0 */
+   if (vao->BufferBinding[0].BufferObj != bo)
+      return false;
+   /* BufferBinding[0].Offset != buffer_offset is checked per attribute */
+   if (vao->BufferBinding[0].Stride != stride)
+      return false;
+   assert(vao->BufferBinding[0].InstanceDivisor == 0);
+
+   /* Retrieve the mapping from VBO_ATTRIB to VERT_ATTRIB space */
+   const GLubyte *const vao_to_vbo_map = _vbo_attribute_alias_map[mode];
+
+   /* Now check the enabled arrays */
+   GLbitfield mask = vao_enabled;
+   while (mask) {
+      const int attr = u_bit_scan(&mask);
+      const unsigned char vbo_attr = vao_to_vbo_map[attr];
+      const GLenum16 tp = type[vbo_attr];
+      const GLintptr off = offset[vbo_attr] + buffer_offset;
+      const struct gl_array_attributes *attrib = &vao->VertexAttrib[attr];
+      if (attrib->RelativeOffset + vao->BufferBinding[0].Offset != off)
+         return false;
+      if (attrib->Type != tp)
+         return false;
+      if (attrib->Size != size[vbo_attr])
+         return false;
+      assert(attrib->Format == GL_RGBA);
+      assert(attrib->Enabled == GL_TRUE);
+      assert(attrib->Normalized == GL_FALSE);
+      assert(attrib->Integer == vbo_attrtype_to_integer_flag(tp));
+      assert(attrib->Doubles == vbo_attrtype_to_double_flag(tp));
+      assert(attrib->BufferBindingIndex == 0);
+   }
+
+   return true;
+}
+
+
+/* Create or reuse the vao for the vertex processing mode. */
+static void
+update_vao(struct gl_context *ctx,
+           gl_vertex_processing_mode mode,
+           struct gl_vertex_array_object **vao,
+           struct gl_buffer_object *bo, GLintptr buffer_offset,
+           GLuint stride, GLbitfield64 vbo_enabled,
+           const GLubyte size[VBO_ATTRIB_MAX],
+           const GLenum16 type[VBO_ATTRIB_MAX],
+           const GLuint offset[VBO_ATTRIB_MAX])
+{
+   /* Compute the bitmasks of vao_enabled arrays */
+   GLbitfield vao_enabled = _vbo_get_vao_enabled_from_vbo(mode, vbo_enabled);
+
+   /*
+    * Check if we can possibly reuse the exisiting one.
+    * In the long term we should reset them when something changes.
+    */
+   if (compare_vao(mode, *vao, bo, buffer_offset, stride,
+                   vao_enabled, size, type, offset))
+      return;
+
+   /* The initial refcount is 1 */
+   _mesa_reference_vao(ctx, vao, NULL);
+   *vao = _mesa_new_vao(ctx, ~((GLuint)0));
+
+   /*
+    * assert(stride <= ctx->Const.MaxVertexAttribStride);
+    * MaxVertexAttribStride is not set for drivers that does not
+    * expose GL 44 or GLES 31.
+    */
+
+   /* Bind the buffer object at binding point 0 */
+   _mesa_bind_vertex_buffer(ctx, *vao, 0, bo, buffer_offset, stride);
+
+   /* Retrieve the mapping from VBO_ATTRIB to VERT_ATTRIB space
+    * Note that the position/generic0 aliasing is done in the VAO.
+    */
+   const GLubyte *const vao_to_vbo_map = _vbo_attribute_alias_map[mode];
+   /* Now set the enable arrays */
+   GLbitfield mask = vao_enabled;
+   while (mask) {
+      const int vao_attr = u_bit_scan(&mask);
+      const GLubyte vbo_attr = vao_to_vbo_map[vao_attr];
+      assert(offset[vbo_attr] <= ctx->Const.MaxVertexAttribRelativeOffset);
+
+      _vbo_set_attrib_format(ctx, *vao, vao_attr, buffer_offset,
+                             size[vbo_attr], type[vbo_attr], offset[vbo_attr]);
+      _mesa_vertex_attrib_binding(ctx, *vao, vao_attr, 0);
+      _mesa_enable_vertex_array_attrib(ctx, *vao, vao_attr);
+   }
+   assert(vao_enabled == (*vao)->_Enabled);
+   assert((vao_enabled & ~(*vao)->VertexAttribBufferMask) == 0);
+
+   /* Finalize and freeze the VAO */
+   _mesa_set_vao_immutable(ctx, *vao);
+}
+
+
 /**
  * Insert the active immediate struct onto the display list currently
  * being built.
@@ -430,53 +549,85 @@ compile_vertex_list(struct gl_context *ctx)
 
    /* Duplicate our template, increment refcounts to the storage structs:
     */
-   node->enabled = save->enabled;
-   memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz));
-   memcpy(node->attrtype, save->attrtype, sizeof(node->attrtype));
-   node->vertex_size = save->vertex_size;
-   node->buffer_offset =
-      (save->buffer_map - save->vertex_store->buffer_map) * sizeof(GLfloat);
+   GLintptr old_offset = 0;
+   if (save->VAO[0]) {
+      old_offset = save->VAO[0]->BufferBinding[0].Offset
+         + save->VAO[0]->VertexAttrib[VERT_ATTRIB_POS].RelativeOffset;
+   }
+   const GLsizei stride = save->vertex_size*sizeof(GLfloat);
+   GLintptr buffer_offset =
+       (save->buffer_map - save->vertex_store->buffer_map) * sizeof(GLfloat);
+   assert(old_offset <= buffer_offset);
+   const GLintptr offset_diff = buffer_offset - old_offset;
+   GLuint start_offset = 0;
+   if (offset_diff > 0 && stride > 0 && offset_diff % stride == 0) {
+      /* The vertex size is an exact multiple of the buffer offset.
+       * This means that we can use zero-based vertex attribute pointers
+       * and specify the start of the primitive with the _mesa_prim::start
+       * field.  This results in issuing several draw calls with identical
+       * vertex attribute information.  This can result in fewer state
+       * changes in drivers.  In particular, the Gallium CSO module will
+       * filter out redundant vertex buffer changes.
+       */
+      /* We cannot immediately update the primitives as some methods below
+       * still need the uncorrected start vertices
+       */
+      start_offset = offset_diff/stride;
+      assert(old_offset == buffer_offset - offset_diff);
+      buffer_offset = old_offset;
+   }
+   GLuint offsets[VBO_ATTRIB_MAX];
+   for (unsigned i = 0, offset = 0; i < VBO_ATTRIB_MAX; ++i) {
+      offsets[i] = offset;
+      offset += save->attrsz[i] * sizeof(GLfloat);
+   }
    node->vertex_count = save->vert_count;
    node->wrap_count = save->copied.nr;
-   node->dangling_attr_ref = save->dangling_attr_ref;
    node->prims = save->prims;
    node->prim_count = save->prim_count;
-   node->vertex_store = save->vertex_store;
    node->prim_store = save->prim_store;
 
-   node->vertex_store->refcount++;
+   /* Create a pair of VAOs for the possible VERTEX_PROCESSING_MODEs
+    * Note that this may reuse the previous one of possible.
+    */
+   for (gl_vertex_processing_mode vpm = VP_MODE_FF; vpm < VP_MODE_MAX; ++vpm) {
+      /* create or reuse the vao */
+      update_vao(ctx, vpm, &save->VAO[vpm],
+                 save->vertex_store->bufferobj, buffer_offset, stride,
+                 save->enabled, save->attrsz, save->attrtype, offsets);
+      /* Reference the vao in the dlist */
+      node->VAO[vpm] = NULL;
+      _mesa_reference_vao(ctx, &node->VAO[vpm], save->VAO[vpm]);
+   }
+
    node->prim_store->refcount++;
 
    if (node->prims[0].no_current_update) {
-      node->current_size = 0;
       node->current_data = NULL;
    }
    else {
-      node->current_size = node->vertex_size - node->attrsz[0];
+      GLuint current_size = save->vertex_size - save->attrsz[0];
       node->current_data = NULL;
 
-      if (node->current_size) {
-         /* If the malloc fails, we just pull the data out of the VBO
-          * later instead.
-          */
-         node->current_data = malloc(node->current_size * sizeof(GLfloat));
+      if (current_size) {
+         node->current_data = malloc(current_size * sizeof(GLfloat));
          if (node->current_data) {
-            const char *buffer = (const char *) save->vertex_store->buffer_map;
-            unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat);
+            const char *buffer = (const char *)save->buffer_map;
+            unsigned attr_offset = save->attrsz[0] * sizeof(GLfloat);
             unsigned vertex_offset = 0;
 
             if (node->vertex_count)
-               vertex_offset =
-                  (node->vertex_count - 1) * node->vertex_size * sizeof(GLfloat);
+               vertex_offset = (node->vertex_count - 1) * stride;
 
-            memcpy(node->current_data,
-                   buffer + node->buffer_offset + vertex_offset + attr_offset,
-                   node->current_size * sizeof(GLfloat));
+            memcpy(node->current_data, buffer + vertex_offset + attr_offset,
+                   current_size * sizeof(GLfloat));
+         } else {
+            _mesa_error(ctx, GL_OUT_OF_MEMORY, "Current value allocation");
          }
       }
    }
 
-   assert(node->attrsz[VBO_ATTRIB_POS] != 0 || node->vertex_count == 0);
+   assert(save->attrsz[VBO_ATTRIB_POS] != 0 || node->vertex_count == 0);
 
    if (save->dangling_attr_ref)
       ctx->ListState.CurrentList->Flags |= DLIST_DANGLING_REFS;
@@ -494,6 +645,15 @@ compile_vertex_list(struct gl_context *ctx)
 
    merge_prims(node->prims, &node->prim_count);
 
+   /* Correct the primitive starts, we can only do this here as copy_vertices
+    * and convert_line_loop_to_strip above consume the uncorrected starts.
+    * On the other hand the _vbo_loopback_vertex_list call below needs the
+    * primitves to be corrected already.
+    */
+   for (unsigned i = 0; i < node->prim_count; i++) {
+      node->prims[i].start += start_offset;
+   }
+
    /* Deal with GL_COMPILE_AND_EXECUTE:
     */
    if (ctx->ExecuteFlag) {
@@ -501,13 +661,8 @@ compile_vertex_list(struct gl_context *ctx)
 
       _glapi_set_dispatch(ctx->Exec);
 
-      const GLfloat *buffer = (const GLfloat *)
-         ((const char *) save->vertex_store->buffer_map +
-          node->buffer_offset);
-
-      vbo_loopback_vertex_list(ctx, buffer,
-                               node->attrsz, node->prims, node->prim_count,
-                               node->wrap_count, node->vertex_size);
+      /* Note that the range of referenced vertices must be mapped already */
+      _vbo_loopback_vertex_list(ctx, node);
 
       _glapi_set_dispatch(dispatch);
    }
@@ -524,9 +679,11 @@ compile_vertex_list(struct gl_context *ctx)
 
       /* Release old reference:
        */
-      save->vertex_store->refcount--;
-      assert(save->vertex_store->refcount != 0);
+      free_vertex_store(ctx, save->vertex_store);
       save->vertex_store = NULL;
+      /* When we have a new vbo, we will for sure need a new vao */
+      for (gl_vertex_processing_mode vpm = 0; vpm < VP_MODE_MAX; ++vpm)
+         _mesa_reference_vao(ctx, &save->VAO[vpm], NULL);
 
       /* Allocate and map new store:
        */
@@ -563,7 +720,6 @@ wrap_buffers(struct gl_context *ctx)
    struct vbo_save_context *save = &vbo_context(ctx)->save;
    GLint i = save->prim_count - 1;
    GLenum mode;
-   GLboolean weak;
    GLboolean no_current_update;
 
    assert(i < (GLint) save->prim_max);
@@ -573,7 +729,6 @@ wrap_buffers(struct gl_context *ctx)
     */
    save->prims[i].count = (save->vert_count - save->prims[i].start);
    mode = save->prims[i].mode;
-   weak = save->prims[i].weak;
    no_current_update = save->prims[i].no_current_update;
 
    /* store the copied vertices, and allocate a new list.
@@ -583,7 +738,6 @@ wrap_buffers(struct gl_context *ctx)
    /* Restart interrupted primitive
     */
    save->prims[0].mode = mode;
-   save->prims[0].weak = weak;
    save->prims[0].no_current_update = no_current_update;
    save->prims[0].begin = 0;
    save->prims[0].end = 0;
@@ -634,9 +788,12 @@ copy_to_current(struct gl_context *ctx)
       const int i = u_bit_scan64(&enabled);
       assert(save->attrsz[i]);
 
-      save->currentsz[i][0] = save->attrsz[i];
-      COPY_CLEAN_4V_TYPE_AS_UNION(save->current[i], save->attrsz[i],
-                                  save->attrptr[i], save->attrtype[i]);
+      if (save->attrtype[i] == GL_DOUBLE ||
+          save->attrtype[i] == GL_UNSIGNED_INT64_ARB)
+         memcpy(save->current[i], save->attrptr[i], save->attrsz[i] * sizeof(GLfloat));
+      else
+         COPY_CLEAN_4V_TYPE_AS_UNION(save->current[i], save->attrsz[i],
+                                     save->attrptr[i], save->attrtype[i]);
    }
 }
 
@@ -661,8 +818,7 @@ copy_from_current(struct gl_context *ctx)
          save->attrptr[i][0] = save->current[i][0];
          break;
       case 0:
-         assert(0);
-         break;
+         unreachable("Unexpected vertex attribute size");
       }
    }
 }
@@ -779,11 +935,13 @@ upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
  * get a glTexCoord4f() or glTexCoord1f() call.
  */
 static void
-fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint sz)
+fixup_vertex(struct gl_context *ctx, GLuint attr,
+             GLuint sz, GLenum newType)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
 
-   if (sz > save->attrsz[attr]) {
+   if (sz > save->attrsz[attr] ||
+       newType != save->attrtype[attr]) {
       /* New size is larger.  Need to flush existing vertices and get
        * an enlarged vertex format.
        */
@@ -838,9 +996,10 @@ reset_vertex(struct gl_context *ctx)
 #define ATTR_UNION(A, N, T, C, V0, V1, V2, V3)                 \
 do {                                                           \
    struct vbo_save_context *save = &vbo_context(ctx)->save;    \
+   int sz = (sizeof(C) / sizeof(GLfloat));                     \
                                                                \
    if (save->active_sz[A] != N)                                        \
-      fixup_vertex(ctx, A, N);                                 \
+      fixup_vertex(ctx, A, N * sz, T);                         \
                                                                \
    {                                                           \
       C *dest = (C *)save->attrptr[A];                          \
@@ -1046,7 +1205,6 @@ vbo_save_NotifyBegin(struct gl_context *ctx, GLenum mode)
    save->prims[i].mode = mode & VBO_SAVE_PRIM_MODE_MASK;
    save->prims[i].begin = 1;
    save->prims[i].end = 0;
-   save->prims[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0;
    save->prims[i].no_current_update =
       (mode & VBO_SAVE_PRIM_NO_CURRENT_UPDATE) ? 1 : 0;
    save->prims[i].pad = 0;
@@ -1138,7 +1296,7 @@ static void GLAPIENTRY
 _save_OBE_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
 {
    GET_CURRENT_CONTEXT(ctx);
-   vbo_save_NotifyBegin(ctx, GL_QUADS | VBO_SAVE_PRIM_WEAK);
+   vbo_save_NotifyBegin(ctx, GL_QUADS);
    CALL_Vertex2f(GET_DISPATCH(), (x1, y1));
    CALL_Vertex2f(GET_DISPATCH(), (x2, y1));
    CALL_Vertex2f(GET_DISPATCH(), (x2, y2));
@@ -1171,8 +1329,7 @@ _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
 
    _ae_map_vbos(ctx);
 
-   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK
-                              | VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
+   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
 
    for (i = 0; i < count; i++)
       CALL_ArrayElement(GET_DISPATCH(), (start + i));
@@ -1255,8 +1412,7 @@ _save_OBE_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type,
       indices =
          ADD_POINTERS(indexbuf->Mappings[MAP_INTERNAL].Pointer, indices);
 
-   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK |
-                              VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
+   vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
 
    switch (type) {
    case GL_UNSIGNED_BYTE:
@@ -1559,6 +1715,9 @@ vbo_save_SaveFlushVertices(struct gl_context *ctx)
 }
 
 
+/**
+ * Called from glNewList when we're starting to compile a display list.
+ */
 void
 vbo_save_NewList(struct gl_context *ctx, GLuint list, GLenum mode)
 {
@@ -1581,6 +1740,9 @@ vbo_save_NewList(struct gl_context *ctx, GLuint list, GLenum mode)
 }
 
 
+/**
+ * Called from glEndList when we're finished compiling a display list.
+ */
 void
 vbo_save_EndList(struct gl_context *ctx)
 {
@@ -1615,6 +1777,10 @@ vbo_save_EndList(struct gl_context *ctx)
 }
 
 
+/**
+ * Called from the display list code when we're about to execute a
+ * display list.
+ */
 void
 vbo_save_BeginCallList(struct gl_context *ctx, struct gl_display_list *dlist)
 {
@@ -1623,28 +1789,30 @@ vbo_save_BeginCallList(struct gl_context *ctx, struct gl_display_list *dlist)
 }
 
 
+/**
+ * Called from the display list code when we're finished executing a
+ * display list.
+ */
 void
 vbo_save_EndCallList(struct gl_context *ctx)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
 
-   if (ctx->ListState.CallDepth == 1) {
-      /* This is correct: want to keep only the VBO_SAVE_FALLBACK
-       * flag, if it is set:
-       */
-      save->replay_flags &= VBO_SAVE_FALLBACK;
-   }
+   if (ctx->ListState.CallDepth == 1)
+      save->replay_flags = 0;
 }
 
 
+/**
+ * Called by display list code when a display list is being deleted.
+ */
 static void
 vbo_destroy_vertex_list(struct gl_context *ctx, void *data)
 {
    struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
-   (void) ctx;
 
-   if (--node->vertex_store->refcount == 0)
-      free_vertex_store(ctx, node->vertex_store);
+   for (gl_vertex_processing_mode vpm = VP_MODE_FF; vpm < VP_MODE_MAX; ++vpm)
+      _mesa_reference_vao(ctx, &node->VAO[vpm], NULL);
 
    if (--node->prim_store->refcount == 0)
       free(node->prim_store);
@@ -1659,21 +1827,20 @@ vbo_print_vertex_list(struct gl_context *ctx, void *data, FILE *f)
 {
    struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
    GLuint i;
-   struct gl_buffer_object *buffer = node->vertex_store ?
-      node->vertex_store->bufferobj : NULL;
+   struct gl_buffer_object *buffer = node->VAO[0]->BufferBinding[0].BufferObj;
+   const GLuint vertex_size = _vbo_save_get_stride(node)/sizeof(GLfloat);
    (void) ctx;
 
    fprintf(f, "VBO-VERTEX-LIST, %u vertices, %d primitives, %d vertsize, "
            "buffer %p\n",
-           node->vertex_count, node->prim_count, node->vertex_size,
+           node->vertex_count, node->prim_count, vertex_size,
            buffer);
 
    for (i = 0; i < node->prim_count; i++) {
       struct _mesa_prim *prim = &node->prims[i];
-      fprintf(f, "   prim %d: %s%s %d..%d %s %s\n",
+      fprintf(f, "   prim %d: %s %d..%d %s %s\n",
              i,
              _mesa_lookup_prim_by_nr(prim->mode),
-             prim->weak ? " (weak)" : "",
              prim->start,
              prim->start + prim->count,
              (prim->begin) ? "BEGIN" : "(wrap)",
@@ -1714,7 +1881,6 @@ void
 vbo_save_api_init(struct vbo_save_context *save)
 {
    struct gl_context *ctx = save->ctx;
-   GLuint i;
 
    save->opcode_vertex_list =
       _mesa_dlist_alloc_opcode(ctx,
@@ -1726,8 +1892,4 @@ vbo_save_api_init(struct vbo_save_context *save)
    vtxfmt_init(ctx);
    current_init(ctx);
    _mesa_noop_vtxfmt_init(&save->vtxfmt_noop);
-
-   /* These will actually get set again when binding/drawing */
-   for (i = 0; i < VBO_ATTRIB_MAX; i++)
-      save->inputs[i] = &save->arrays[i];
 }