mesa: add support for memory object creation/import/delete
[mesa.git] / src / mesa / main / marshal.c
index 14577dd4cb4ed39231a285b0603eff5fd7114eb2..8f8e8c78ed21e23ef5dcedb2a4d560959efd78f7 100644 (file)
@@ -27,6 +27,8 @@
  * thread when automatic code generation isn't appropriate.
  */
 
+#include "main/enums.h"
+#include "main/macros.h"
 #include "marshal.h"
 #include "dispatch.h"
 #include "marshal_generated.h"
@@ -63,6 +65,43 @@ _mesa_marshal_Flush(void)
    _mesa_glthread_flush_batch(ctx);
 }
 
+/* Enable: marshalled asynchronously */
+struct marshal_cmd_Enable
+{
+   struct marshal_cmd_base cmd_base;
+   GLenum cap;
+};
+
+void
+_mesa_unmarshal_Enable(struct gl_context *ctx,
+                       const struct marshal_cmd_Enable *cmd)
+{
+   const GLenum cap = cmd->cap;
+   CALL_Enable(ctx->CurrentServerDispatch, (cap));
+}
+
+void GLAPIENTRY
+_mesa_marshal_Enable(GLenum cap)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct marshal_cmd_Enable *cmd;
+   debug_print_marshal("Enable");
+
+   if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) {
+      _mesa_glthread_finish(ctx);
+      _mesa_glthread_restore_dispatch(ctx);
+   } else {
+      cmd = _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_Enable,
+                                            sizeof(*cmd));
+      cmd->cap = cap;
+      _mesa_post_marshal_hook(ctx);
+      return;
+   }
+
+   _mesa_glthread_finish(ctx);
+   debug_print_sync_fallback("Enable");
+   CALL_Enable(ctx->CurrentServerDispatch, (cap));
+}
 
 struct marshal_cmd_ShaderSource
 {
@@ -154,3 +193,557 @@ _mesa_marshal_ShaderSource(GLuint shader, GLsizei count,
    }
    free(length_tmp);
 }
+
+
+/* BindBufferBase: marshalled asynchronously */
+struct marshal_cmd_BindBufferBase
+{
+   struct marshal_cmd_base cmd_base;
+   GLenum target;
+   GLuint index;
+   GLuint buffer;
+};
+
+/** Tracks the current bindings for the vertex array and index array buffers.
+ *
+ * This is part of what we need to enable glthread on compat-GL contexts that
+ * happen to use VBOs, without also supporting the full tracking of VBO vs
+ * user vertex array bindings per attribute on each vertex array for
+ * determining what to upload at draw call time.
+ *
+ * Note that GL core makes it so that a buffer binding with an invalid handle
+ * in the "buffer" parameter will throw an error, and then a
+ * glVertexAttribPointer() that followsmight not end up pointing at a VBO.
+ * However, in GL core the draw call would throw an error as well, so we don't
+ * really care if our tracking is wrong for this case -- we never need to
+ * marshal user data for draw calls, and the unmarshal will just generate an
+ * error or not as appropriate.
+ *
+ * For compatibility GL, we do need to accurately know whether the draw call
+ * on the unmarshal side will dereference a user pointer or load data from a
+ * VBO per vertex.  That would make it seem like we need to track whether a
+ * "buffer" is valid, so that we can know when an error will be generated
+ * instead of updating the binding.  However, compat GL has the ridiculous
+ * 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.
+ */
+static void
+track_vbo_binding(struct gl_context *ctx, GLenum target, GLuint buffer)
+{
+   struct glthread_state *glthread = ctx->GLThread;
+
+   switch (target) {
+   case GL_ARRAY_BUFFER:
+      glthread->vertex_array_is_vbo = (buffer != 0);
+      break;
+   case GL_ELEMENT_ARRAY_BUFFER:
+      /* The current element array buffer binding is actually tracked in the
+       * vertex array object instead of the context, so this would need to
+       * change on vertex array object updates.
+       */
+      glthread->element_array_is_vbo = (buffer != 0);
+      break;
+   }
+}
+
+
+struct marshal_cmd_BindBuffer
+{
+   struct marshal_cmd_base cmd_base;
+   GLenum target;
+   GLuint buffer;
+};
+
+/**
+ * This is just like the code-generated glBindBuffer() support, except that we
+ * call track_vbo_binding().
+ */
+void
+_mesa_unmarshal_BindBuffer(struct gl_context *ctx,
+                           const struct marshal_cmd_BindBuffer *cmd)
+{
+   const GLenum target = cmd->target;
+   const GLuint buffer = cmd->buffer;
+   CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
+}
+void GLAPIENTRY
+_mesa_marshal_BindBuffer(GLenum target, GLuint buffer)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   size_t cmd_size = sizeof(struct marshal_cmd_BindBuffer);
+   struct marshal_cmd_BindBuffer *cmd;
+   debug_print_marshal("BindBuffer");
+
+   track_vbo_binding(ctx, target, buffer);
+
+   if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
+      cmd = _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BindBuffer,
+                                            cmd_size);
+      cmd->target = target;
+      cmd->buffer = buffer;
+      _mesa_post_marshal_hook(ctx);
+   } else {
+      _mesa_glthread_finish(ctx);
+      CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
+   }
+}
+
+/* BufferData: marshalled asynchronously */
+struct marshal_cmd_BufferData
+{
+   struct marshal_cmd_base cmd_base;
+   GLenum target;
+   GLsizeiptr size;
+   GLenum usage;
+   bool data_null; /* If set, no data follows for "data" */
+   /* Next size bytes are GLubyte data[size] */
+};
+
+void
+_mesa_unmarshal_BufferData(struct gl_context *ctx,
+                           const struct marshal_cmd_BufferData *cmd)
+{
+   const GLenum target = cmd->target;
+   const GLsizeiptr size = cmd->size;
+   const GLenum usage = cmd->usage;
+   const void *data;
+
+   if (cmd->data_null)
+      data = NULL;
+   else
+      data = (const void *) (cmd + 1);
+
+   CALL_BufferData(ctx->CurrentServerDispatch, (target, size, data, usage));
+}
+
+void GLAPIENTRY
+_mesa_marshal_BufferData(GLenum target, GLsizeiptr size, const GLvoid * data,
+                         GLenum usage)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   size_t cmd_size =
+      sizeof(struct marshal_cmd_BufferData) + (data ? size : 0);
+   debug_print_marshal("BufferData");
+
+   if (unlikely(size < 0)) {
+      _mesa_glthread_finish(ctx);
+      _mesa_error(ctx, GL_INVALID_VALUE, "BufferData(size < 0)");
+      return;
+   }
+
+   if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
+       cmd_size <= MARSHAL_MAX_CMD_SIZE) {
+      struct marshal_cmd_BufferData *cmd =
+         _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferData,
+                                         cmd_size);
+
+      cmd->target = target;
+      cmd->size = size;
+      cmd->usage = usage;
+      cmd->data_null = !data;
+      if (data) {
+         char *variable_data = (char *) (cmd + 1);
+         memcpy(variable_data, data, size);
+      }
+      _mesa_post_marshal_hook(ctx);
+   } else {
+      _mesa_glthread_finish(ctx);
+      CALL_BufferData(ctx->CurrentServerDispatch,
+                      (target, size, data, usage));
+   }
+}
+
+/* BufferSubData: marshalled asynchronously */
+struct marshal_cmd_BufferSubData
+{
+   struct marshal_cmd_base cmd_base;
+   GLenum target;
+   GLintptr offset;
+   GLsizeiptr size;
+   /* Next size bytes are GLubyte data[size] */
+};
+
+void
+_mesa_unmarshal_BufferSubData(struct gl_context *ctx,
+                              const struct marshal_cmd_BufferSubData *cmd)
+{
+   const GLenum target = cmd->target;
+   const GLintptr offset = cmd->offset;
+   const GLsizeiptr size = cmd->size;
+   const void *data = (const void *) (cmd + 1);
+
+   CALL_BufferSubData(ctx->CurrentServerDispatch,
+                      (target, offset, size, data));
+}
+
+void GLAPIENTRY
+_mesa_marshal_BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
+                            const GLvoid * data)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   size_t cmd_size = sizeof(struct marshal_cmd_BufferSubData) + size;
+
+   debug_print_marshal("BufferSubData");
+   if (unlikely(size < 0)) {
+      _mesa_glthread_finish(ctx);
+      _mesa_error(ctx, GL_INVALID_VALUE, "BufferSubData(size < 0)");
+      return;
+   }
+
+   if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
+       cmd_size <= MARSHAL_MAX_CMD_SIZE) {
+      struct marshal_cmd_BufferSubData *cmd =
+         _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferSubData,
+                                         cmd_size);
+      cmd->target = target;
+      cmd->offset = offset;
+      cmd->size = size;
+      char *variable_data = (char *) (cmd + 1);
+      memcpy(variable_data, data, size);
+      _mesa_post_marshal_hook(ctx);
+   } else {
+      _mesa_glthread_finish(ctx);
+      CALL_BufferSubData(ctx->CurrentServerDispatch,
+                         (target, offset, size, data));
+   }
+}
+
+/* NamedBufferData: marshalled asynchronously */
+struct marshal_cmd_NamedBufferData
+{
+   struct marshal_cmd_base cmd_base;
+   GLuint name;
+   GLsizei size;
+   GLenum usage;
+   bool data_null; /* If set, no data follows for "data" */
+   /* Next size bytes are GLubyte data[size] */
+};
+
+void
+_mesa_unmarshal_NamedBufferData(struct gl_context *ctx,
+                                const struct marshal_cmd_NamedBufferData *cmd)
+{
+   const GLuint name = cmd->name;
+   const GLsizei size = cmd->size;
+   const GLenum usage = cmd->usage;
+   const void *data;
+
+   if (cmd->data_null)
+      data = NULL;
+   else
+      data = (const void *) (cmd + 1);
+
+   CALL_NamedBufferData(ctx->CurrentServerDispatch,
+                        (name, size, data, usage));
+}
+
+void GLAPIENTRY
+_mesa_marshal_NamedBufferData(GLuint buffer, GLsizeiptr size,
+                              const GLvoid * data, GLenum usage)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   size_t cmd_size = sizeof(struct marshal_cmd_NamedBufferData) + (data ? size : 0);
+
+   debug_print_marshal("NamedBufferData");
+   if (unlikely(size < 0)) {
+      _mesa_glthread_finish(ctx);
+      _mesa_error(ctx, GL_INVALID_VALUE, "NamedBufferData(size < 0)");
+      return;
+   }
+
+   if (buffer > 0 && cmd_size <= MARSHAL_MAX_CMD_SIZE) {
+      struct marshal_cmd_NamedBufferData *cmd =
+         _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_NamedBufferData,
+                                         cmd_size);
+      cmd->name = buffer;
+      cmd->size = size;
+      cmd->usage = usage;
+      cmd->data_null = !data;
+      if (data) {
+         char *variable_data = (char *) (cmd + 1);
+         memcpy(variable_data, data, size);
+      }
+      _mesa_post_marshal_hook(ctx);
+   } else {
+      _mesa_glthread_finish(ctx);
+      CALL_NamedBufferData(ctx->CurrentServerDispatch,
+                           (buffer, size, data, usage));
+   }
+}
+
+/* NamedBufferSubData: marshalled asynchronously */
+struct marshal_cmd_NamedBufferSubData
+{
+   struct marshal_cmd_base cmd_base;
+   GLuint name;
+   GLintptr offset;
+   GLsizei size;
+   /* Next size bytes are GLubyte data[size] */
+};
+
+void
+_mesa_unmarshal_NamedBufferSubData(struct gl_context *ctx,
+                                   const struct marshal_cmd_NamedBufferSubData *cmd)
+{
+   const GLuint name = cmd->name;
+   const GLintptr offset = cmd->offset;
+   const GLsizei size = cmd->size;
+   const void *data = (const void *) (cmd + 1);
+
+   CALL_NamedBufferSubData(ctx->CurrentServerDispatch,
+                           (name, offset, size, data));
+}
+
+void GLAPIENTRY
+_mesa_marshal_NamedBufferSubData(GLuint buffer, GLintptr offset,
+                                 GLsizeiptr size, const GLvoid * data)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   size_t cmd_size = sizeof(struct marshal_cmd_NamedBufferSubData) + size;
+
+   debug_print_marshal("NamedBufferSubData");
+   if (unlikely(size < 0)) {
+      _mesa_glthread_finish(ctx);
+      _mesa_error(ctx, GL_INVALID_VALUE, "NamedBufferSubData(size < 0)");
+      return;
+   }
+
+   if (buffer > 0 && cmd_size <= MARSHAL_MAX_CMD_SIZE) {
+      struct marshal_cmd_NamedBufferSubData *cmd =
+         _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_NamedBufferSubData,
+                                         cmd_size);
+      cmd->name = buffer;
+      cmd->offset = offset;
+      cmd->size = size;
+      char *variable_data = (char *) (cmd + 1);
+      memcpy(variable_data, data, size);
+      _mesa_post_marshal_hook(ctx);
+   } else {
+      _mesa_glthread_finish(ctx);
+      CALL_NamedBufferSubData(ctx->CurrentServerDispatch,
+                              (buffer, offset, size, data));
+   }
+}
+
+/* ClearBuffer* (all variants): marshalled asynchronously */
+struct marshal_cmd_ClearBuffer
+{
+   struct marshal_cmd_base cmd_base;
+   GLenum buffer;
+   GLint drawbuffer;
+};
+
+void
+_mesa_unmarshal_ClearBufferfv(struct gl_context *ctx,
+                              const struct marshal_cmd_ClearBuffer *cmd)
+{
+   const GLenum buffer = cmd->buffer;
+   const GLint drawbuffer = cmd->drawbuffer;
+   const char *variable_data = (const char *) (cmd + 1);
+   const GLfloat *value = (const GLfloat *) variable_data;
+
+   CALL_ClearBufferfv(ctx->CurrentServerDispatch,
+                      (buffer, drawbuffer, value));
+}
+
+void
+_mesa_unmarshal_ClearBufferiv(struct gl_context *ctx,
+                              const struct marshal_cmd_ClearBuffer *cmd)
+{
+   const GLenum buffer = cmd->buffer;
+   const GLint drawbuffer = cmd->drawbuffer;
+   const char *variable_data = (const char *) (cmd + 1);
+   const GLint *value = (const GLint *) variable_data;
+
+   CALL_ClearBufferiv(ctx->CurrentServerDispatch,
+                      (buffer, drawbuffer, value));
+}
+
+void
+_mesa_unmarshal_ClearBufferuiv(struct gl_context *ctx,
+                               const struct marshal_cmd_ClearBuffer *cmd)
+{
+   const GLenum buffer = cmd->buffer;
+   const GLint drawbuffer = cmd->drawbuffer;
+   const char *variable_data = (const char *) (cmd + 1);
+   const GLuint *value = (const GLuint *) variable_data;
+
+   CALL_ClearBufferuiv(ctx->CurrentServerDispatch,
+                       (buffer, drawbuffer, value));
+}
+
+void
+_mesa_unmarshal_ClearBufferfi(struct gl_context *ctx,
+                              const struct marshal_cmd_ClearBuffer *cmd)
+{
+   const GLenum buffer = cmd->buffer;
+   const GLint drawbuffer = cmd->drawbuffer;
+   const char *variable_data = (const char *) (cmd + 1);
+   const GLfloat *depth = (const GLfloat *) variable_data;
+   const GLint *stencil = (const GLint *) (variable_data + 4);
+
+   CALL_ClearBufferfi(ctx->CurrentServerDispatch,
+                      (buffer, drawbuffer, *depth, *stencil));
+}
+
+static inline size_t buffer_to_size(GLenum buffer)
+{
+   switch (buffer) {
+   case GL_COLOR:
+      return 4;
+   case GL_DEPTH_STENCIL:
+      return 2;
+   case GL_STENCIL:
+   case GL_DEPTH:
+      return 1;
+   default:
+      return 0;
+   }
+}
+
+static inline bool clear_buffer_add_command(struct gl_context *ctx, uint16_t id,
+                                            GLenum buffer, GLint drawbuffer,
+                                            const GLuint *value, size_t size)
+{
+   size_t cmd_size = sizeof(struct marshal_cmd_ClearBuffer) + 4 * size;
+   if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
+      struct marshal_cmd_ClearBuffer *cmd =
+         _mesa_glthread_allocate_command(ctx, id,
+                                         cmd_size);
+      cmd->buffer = buffer;
+      cmd->drawbuffer = drawbuffer;
+      GLuint *variable_data = (GLuint *) (cmd + 1);
+      if (size == 4)
+         COPY_4V(variable_data,  value);
+      else if (size == 2)
+         COPY_2V(variable_data, value);
+      else
+         *variable_data = *value;
+
+      _mesa_post_marshal_hook(ctx);
+      return true;
+   }
+
+   return false;
+}
+
+void GLAPIENTRY
+_mesa_marshal_ClearBufferfv(GLenum buffer, GLint drawbuffer,
+                            const GLfloat *value)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   debug_print_marshal("ClearBufferfv");
+
+   if (!(buffer == GL_DEPTH || buffer == GL_COLOR)) {
+      _mesa_glthread_finish(ctx);
+
+      /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers'
+       * of the OpenGL 4.5 spec states:
+       *
+       *    "An INVALID_ENUM error is generated by ClearBufferfv and
+       *     ClearNamedFramebufferfv if buffer is not COLOR or DEPTH."
+       */
+      _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferfv(buffer=%s)",
+                  _mesa_enum_to_string(buffer));
+   }
+
+   size_t size = buffer_to_size(buffer);
+   if (!clear_buffer_add_command(ctx, DISPATCH_CMD_ClearBufferfv, buffer,
+                                 drawbuffer, (GLuint *)value, size)) {
+      debug_print_sync("ClearBufferfv");
+      _mesa_glthread_finish(ctx);
+      CALL_ClearBufferfv(ctx->CurrentServerDispatch,
+                         (buffer, drawbuffer, value));
+   }
+}
+
+void GLAPIENTRY
+_mesa_marshal_ClearBufferiv(GLenum buffer, GLint drawbuffer,
+                            const GLint *value)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   debug_print_marshal("ClearBufferiv");
+
+   if (!(buffer == GL_STENCIL || buffer == GL_COLOR)) {
+      _mesa_glthread_finish(ctx);
+
+      /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers'
+       * of the OpenGL 4.5 spec states:
+       *
+       *    "An INVALID_ENUM error is generated by ClearBufferiv and
+       *     ClearNamedFramebufferiv if buffer is not COLOR or STENCIL."
+       */
+      _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferiv(buffer=%s)",
+                  _mesa_enum_to_string(buffer));
+   }
+
+   size_t size = buffer_to_size(buffer);
+   if (!clear_buffer_add_command(ctx, DISPATCH_CMD_ClearBufferiv, buffer,
+                                 drawbuffer, (GLuint *)value, size)) {
+      debug_print_sync("ClearBufferiv");
+      _mesa_glthread_finish(ctx);
+      CALL_ClearBufferiv(ctx->CurrentServerDispatch,
+                         (buffer, drawbuffer, value));
+   }
+}
+
+void GLAPIENTRY
+_mesa_marshal_ClearBufferuiv(GLenum buffer, GLint drawbuffer,
+                             const GLuint *value)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   debug_print_marshal("ClearBufferuiv");
+
+   if (buffer != GL_COLOR) {
+      _mesa_glthread_finish(ctx);
+
+      /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers'
+       * of the OpenGL 4.5 spec states:
+       *
+       *    "An INVALID_ENUM error is generated by ClearBufferuiv and
+       *     ClearNamedFramebufferuiv if buffer is not COLOR."
+       */
+      _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferuiv(buffer=%s)",
+                  _mesa_enum_to_string(buffer));
+   }
+
+   if (!clear_buffer_add_command(ctx, DISPATCH_CMD_ClearBufferuiv, buffer,
+                                 drawbuffer, (GLuint *)value, 4)) {
+      debug_print_sync("ClearBufferuiv");
+      _mesa_glthread_finish(ctx);
+      CALL_ClearBufferuiv(ctx->CurrentServerDispatch,
+                         (buffer, drawbuffer, value));
+   }
+}
+
+void GLAPIENTRY
+_mesa_marshal_ClearBufferfi(GLenum buffer, GLint drawbuffer,
+                            const GLfloat depth, const GLint stencil)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   debug_print_marshal("ClearBufferfi");
+
+   if (buffer != GL_DEPTH_STENCIL) {
+      _mesa_glthread_finish(ctx);
+
+      /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers'
+       * of the OpenGL 4.5 spec states:
+       *
+       *    "An INVALID_ENUM error is generated by ClearBufferfi and
+       *     ClearNamedFramebufferfi if buffer is not DEPTH_STENCIL."
+       */
+      _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferfi(buffer=%s)",
+                  _mesa_enum_to_string(buffer));
+   }
+
+   fi_type value[2];
+   value[0].f = depth;
+   value[1].i = stencil;
+   if (!clear_buffer_add_command(ctx, DISPATCH_CMD_ClearBufferfi, buffer,
+                                 drawbuffer, (GLuint *)value, 2)) {
+      debug_print_sync("ClearBufferfi");
+      _mesa_glthread_finish(ctx);
+      CALL_ClearBufferfi(ctx->CurrentServerDispatch,
+                         (buffer, drawbuffer, depth, stencil));
+   }
+}