glthread: merge and collapse glBindBuffer calls that unbind and then bind
authorMarek Olšák <marek.olsak@amd.com>
Mon, 22 Aug 2022 00:32:08 +0000 (20:32 -0400)
committerMarge Bot <emma+marge@anholt.net>
Wed, 19 Oct 2022 04:23:05 +0000 (04:23 +0000)
This is a small optimization for viewperf. See the comment in the code.

Acked-By: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Reviewed-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18199>

src/mapi/glapi/gen/gl_API.xml
src/mesa/main/glthread.c
src/mesa/main/glthread.h
src/mesa/main/glthread_bufferobj.c
src/mesa/main/glthread_list.c
src/mesa/main/glthread_marshal.h

index a72bd9f..c69539c 100644 (file)
     <type name="intptr"   size="4"                  glx_name="CARD32"/>
     <type name="sizeiptr" size="4"  unsigned="true" glx_name="CARD32"/>
 
-    <function name="BindBuffer" es1="1.1" es2="2.0" no_error="true"
-              marshal_call_after="_mesa_glthread_BindBuffer(ctx, target, buffer);">
+    <function name="BindBuffer" es1="1.1" es2="2.0" no_error="true" marshal="custom">
         <param name="target" type="GLenum"/>
         <param name="buffer" type="GLuint"/>
         <glx ignore="true"/>
index c4bdbaa..279d1db 100644 (file)
@@ -246,6 +246,7 @@ _mesa_glthread_flush_batch(struct gl_context *ctx)
       _glapi_set_dispatch(ctx->CurrentClientDispatch);
 
       glthread->LastCallList = NULL;
+      glthread->LastBindBuffer = NULL;
       return;
    }
 
@@ -260,6 +261,7 @@ _mesa_glthread_flush_batch(struct gl_context *ctx)
    glthread->used = 0;
 
    glthread->LastCallList = NULL;
+   glthread->LastBindBuffer = NULL;
 }
 
 /**
@@ -298,6 +300,7 @@ _mesa_glthread_finish(struct gl_context *ctx)
       glthread->used = 0;
 
       glthread->LastCallList = NULL;
+      glthread->LastBindBuffer = NULL;
 
       /* Since glthread_unmarshal_batch changes the dispatch to direct,
        * restore it after it's done.
index c94f63b..d8b7f4b 100644 (file)
@@ -243,6 +243,7 @@ struct glthread_state
 
    /** The last added call of the given function. */
    struct marshal_cmd_CallList *LastCallList;
+   struct marshal_cmd_BindBuffer *LastBindBuffer;
 };
 
 void _mesa_glthread_init(struct gl_context *ctx);
@@ -278,8 +279,6 @@ void _mesa_error_glthread_safe(struct gl_context *ctx, GLenum error,
                                bool glthread, const char *format, ...);
 void _mesa_glthread_execute_list(struct gl_context *ctx, GLuint list);
 
-void _mesa_glthread_BindBuffer(struct gl_context *ctx, GLenum target,
-                               GLuint buffer);
 void _mesa_glthread_DeleteBuffers(struct gl_context *ctx, GLsizei n,
                                   const GLuint *buffers);
 
index 13908b9..80af250 100644 (file)
@@ -173,7 +173,7 @@ _mesa_glthread_upload(struct gl_context *ctx, const void *data,
  * feature that if you pass a bad name, it just gens a buffer object for you,
  * so we escape without having to know if things are valid or not.
  */
-void
+static void
 _mesa_glthread_BindBuffer(struct gl_context *ctx, GLenum target, GLuint buffer)
 {
    struct glthread_state *glthread = &ctx->GLThread;
@@ -204,6 +204,84 @@ _mesa_glthread_BindBuffer(struct gl_context *ctx, GLenum target, GLuint buffer)
    }
 }
 
+/* This can hold up to 2 BindBuffer calls. This is used to eliminate
+ * duplicated BindBuffer calls, which are plentiful in viewperf2020/catia.
+ * In this example, the first 2 calls are eliminated by glthread by keeping
+ * track of the last 2 BindBuffer calls and overwriting them if the target
+ * matches.
+ *
+ *   glBindBuffer(GL_ARRAY_BUFFER, 0);
+ *   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ *   glBindBuffer(GL_ARRAY_BUFFER, 6);
+ *   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 7);
+ */
+struct marshal_cmd_BindBuffer
+{
+   struct marshal_cmd_base cmd_base;
+   GLenum16 target[2];
+   GLuint buffer[2];
+};
+
+uint32_t
+_mesa_unmarshal_BindBuffer(struct gl_context *ctx,
+                           const struct marshal_cmd_BindBuffer *cmd,
+                           const uint64_t *last)
+{
+   CALL_BindBuffer(ctx->CurrentServerDispatch, (cmd->target[0], cmd->buffer[0]));
+
+   if (cmd->target[1])
+      CALL_BindBuffer(ctx->CurrentServerDispatch, (cmd->target[1], cmd->buffer[1]));
+
+   const unsigned cmd_size = (align(sizeof(struct marshal_cmd_BindBuffer), 8) / 8);
+   assert (cmd_size == cmd->cmd_base.cmd_size);
+   return cmd_size;
+}
+
+void GLAPIENTRY
+_mesa_marshal_BindBuffer(GLenum target, GLuint buffer)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct glthread_state *glthread = &ctx->GLThread;
+   struct marshal_cmd_BindBuffer *last = glthread->LastBindBuffer;
+
+   _mesa_glthread_BindBuffer(ctx, target, buffer);
+
+   /* If the last call is BindBuffer... */
+   if (_mesa_glthread_call_is_last(glthread, &last->cmd_base)) {
+      /* If the target is in the last call and unbinding the buffer, overwrite
+       * the buffer ID there.
+       *
+       * We can't optimize out binding non-zero buffers because binding also
+       * creates the GL objects (like glCreateBuffers), which can't be skipped.
+       */
+      if (target == last->target[0] && !last->buffer[0]) {
+         last->buffer[0] = buffer;
+         return;
+      }
+      if (target == last->target[1] && !last->buffer[1]) {
+         last->buffer[1] = buffer;
+         return;
+      }
+
+      /* If the last call has an unused buffer field, add this call to it. */
+      if (last->target[1] == 0) {
+         last->target[1] = MIN2(target, 0xffff); /* clamped to 0xffff (invalid enum) */
+         last->buffer[1] = buffer;
+         return;
+      }
+   }
+
+   int cmd_size = sizeof(struct marshal_cmd_BindBuffer);
+   struct marshal_cmd_BindBuffer *cmd =
+      _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BindBuffer, cmd_size);
+
+   cmd->target[0] = MIN2(target, 0xffff); /* clamped to 0xffff (invalid enum) */
+   cmd->target[1] = 0;
+   cmd->buffer[0] = buffer;
+
+   glthread->LastBindBuffer = cmd;
+}
+
 void
 _mesa_glthread_DeleteBuffers(struct gl_context *ctx, GLsizei n,
                              const GLuint *buffers)
index 32c74ff..7e21487 100644 (file)
@@ -57,9 +57,7 @@ _mesa_marshal_CallList(GLuint list)
    _mesa_glthread_CallList(ctx, list);
 
    /* If the last call is CallList and there is enough space to append another list... */
-   if (last &&
-       (uint64_t*)last + last->cmd_base.cmd_size ==
-       &glthread->next_batch->buffer[glthread->used] &&
+   if (_mesa_glthread_call_is_last(glthread, &last->cmd_base) &&
        glthread->used + 1 <= MARSHAL_MAX_CMD_SIZE / 8) {
       STATIC_ASSERT(sizeof(*last) == 8);
 
index 2b3571f..605e332 100644 (file)
@@ -74,6 +74,15 @@ _mesa_glthread_allocate_command(struct gl_context *ctx,
 }
 
 static inline bool
+_mesa_glthread_call_is_last(struct glthread_state *glthread,
+                            struct marshal_cmd_base *last)
+{
+   return last &&
+          (uint64_t*)last + last->cmd_size ==
+          &glthread->next_batch->buffer[glthread->used];
+}
+
+static inline bool
 _mesa_glthread_has_no_pack_buffer(const struct gl_context *ctx)
 {
    return ctx->GLThread.CurrentPixelPackBufferName == 0;