[Bf-blender-cvs] [6fabca487a3] tmp-drw-callbatching: GPU: Add API to use multidrawindirect using GPUbatch

Clément Foucault noreply at git.blender.org
Sat Aug 17 14:50:31 CEST 2019


Commit: 6fabca487a34dfa01b6dd01841e7ab3c5d2d1c50
Author: Clément Foucault
Date:   Wed Jun 19 15:56:53 2019 +0200
Branches: tmp-drw-callbatching
https://developer.blender.org/rB6fabca487a34dfa01b6dd01841e7ab3c5d2d1c50

GPU: Add API to use multidrawindirect using GPUbatch

This new API record a list of command that use the same batch and submit
it to the GPU in one call.

===================================================================

M	source/blender/gpu/GPU_batch.h
M	source/blender/gpu/intern/gpu_batch.c

===================================================================

diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h
index 175033f70d9..7d8c3347eb4 100644
--- a/source/blender/gpu/GPU_batch.h
+++ b/source/blender/gpu/GPU_batch.h
@@ -193,6 +193,17 @@ GPUBatch *create_BatchInGeneral(GPUPrimType, VertexBufferStuff, ElementListStuff
 
 #endif /* future plans */
 
+/* GPUDrawList is an API to do lots of similar drawcalls very fast using multidrawindirect.
+ * There is a fallback if the feature is not supported. */
+typedef struct GPUDrawList GPUDrawList;
+
+GPUDrawList *GPU_draw_list_create(int length);
+void GPU_draw_list_discard(GPUDrawList *list);
+void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch);
+void GPU_draw_list_command_add(
+    GPUDrawList *list, int v_first, int v_count, int i_first, int i_count);
+void GPU_draw_list_submit(GPUDrawList *list);
+
 void gpu_batch_init(void);
 void gpu_batch_exit(void);
 
diff --git a/source/blender/gpu/intern/gpu_batch.c b/source/blender/gpu/intern/gpu_batch.c
index b279435772b..98ad37e496e 100644
--- a/source/blender/gpu/intern/gpu_batch.c
+++ b/source/blender/gpu/intern/gpu_batch.c
@@ -39,6 +39,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 
 static void batch_update_program_bindings(GPUBatch *batch, uint i_first);
 
@@ -649,6 +650,14 @@ void GPU_batch_draw(GPUBatch *batch)
   GPU_batch_program_use_end(batch);
 }
 
+#if GPU_TRACK_INDEX_RANGE
+#  define BASE_INDEX(el) ((el)->base_index)
+#  define INDEX_TYPE(el) ((el)->gl_index_type)
+#else
+#  define BASE_INDEX(el) 0
+#  define INDEX_TYPE(el) GL_UNSIGNED_INT
+#endif
+
 void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count)
 {
 #if TRUST_NO_ONE
@@ -684,13 +693,8 @@ void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_fi
 
   if (batch->elem) {
     const GPUIndexBuf *el = batch->elem;
-#if GPU_TRACK_INDEX_RANGE
-    GLenum index_type = el->gl_index_type;
-    GLint base_index = el->base_index;
-#else
-    GLenum index_type = GL_UNSIGNED_INT;
-    GLint base_index = 0;
-#endif
+    GLenum index_type = INDEX_TYPE(el);
+    GLint base_index = BASE_INDEX(el);
     void *v_first_ofs = elem_offset(el, v_first);
 
     if (GPU_arb_base_instance_is_supported()) {
@@ -732,6 +736,181 @@ void GPU_draw_primitive(GPUPrimType prim_type, int v_count)
   // glBindVertexArray(0);
 }
 
+/* -------------------------------------------------------------------- */
+/** \name Indirect Draw Calls
+ * \{ */
+
+#if 0
+#  define USE_MULTI_DRAW_INDIRECT 0
+#else
+#  define USE_MULTI_DRAW_INDIRECT (GL_ARB_multi_draw_indirect && GLEW_ARB_base_instance)
+#endif
+
+typedef struct GPUDrawCommand {
+  uint v_count;
+  uint i_count;
+  uint v_first;
+  uint i_first;
+} GPUDrawCommand;
+
+typedef struct GPUDrawCommandIndexed {
+  uint v_count;
+  uint i_count;
+  uint v_first;
+  uint base_index;
+  uint i_first;
+} GPUDrawCommandIndexed;
+
+struct GPUDrawList {
+  GPUBatch *batch;
+  uint base_index;  /* Avoid dereferencing batch. */
+  uint cmd_offset;  /* in bytes, offset  inside indirect command buffer. */
+  uint cmd_len;     /* Number of used command for the next call. */
+  uint buffer_size; /* in bytes, size of indirect command buffer. */
+  GLuint buffer_id; /* Draw Indirect Buffer id */
+  union {
+    GPUDrawCommand *commands;
+    GPUDrawCommandIndexed *commands_indexed;
+  };
+};
+
+GPUDrawList *GPU_draw_list_create(int length)
+{
+  GPUDrawList *list = MEM_mallocN(sizeof(GPUDrawList), "GPUDrawList");
+  /* Alloc the biggest possible command list which is indexed. */
+  list->buffer_size = sizeof(GPUDrawCommandIndexed) * length;
+  list->cmd_len = 0;
+  list->cmd_offset = 0;
+  list->commands = NULL;
+  if (USE_MULTI_DRAW_INDIRECT) {
+    list->buffer_id = GPU_buf_alloc();
+    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
+    glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW);
+  }
+  else {
+    list->commands = MEM_mallocN(list->buffer_size, "GPUDrawList data");
+  }
+  return list;
+}
+
+void GPU_draw_list_discard(GPUDrawList *list)
+{
+  if (list->buffer_id) {
+    GPU_buf_free(list->buffer_id);
+  }
+  else {
+    MEM_SAFE_FREE(list->commands);
+  }
+  MEM_freeN(list);
+}
+
+void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch)
+{
+  BLI_assert(batch->phase == GPU_BATCH_READY_TO_DRAW);
+  list->batch = batch;
+  list->base_index = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX;
+  list->cmd_len = 0;
+
+  if (USE_MULTI_DRAW_INDIRECT) {
+    if (list->commands == NULL) {
+      glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
+      if (list->cmd_offset >= list->buffer_size) {
+        /* Orphan buffer data and start fresh. */
+        glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW);
+        list->cmd_offset = 0;
+      }
+      GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
+      list->commands = glMapBufferRange(
+          GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags);
+    }
+  }
+  else {
+    list->cmd_offset = 0;
+  }
+}
+
+void GPU_draw_list_command_add(
+    GPUDrawList *list, int v_first, int v_count, int i_first, int i_count)
+{
+  BLI_assert(list->commands);
+
+  if (list->base_index != UINT_MAX) {
+    GPUDrawCommandIndexed *cmd = list->commands_indexed + list->cmd_len;
+    cmd->v_first = v_first;
+    cmd->v_count = v_count;
+    cmd->i_count = i_count;
+    cmd->base_index = list->base_index;
+    cmd->i_first = i_first;
+  }
+  else {
+    GPUDrawCommand *cmd = list->commands + list->cmd_len;
+    cmd->v_first = v_first;
+    cmd->v_count = v_count;
+    cmd->i_count = i_count;
+    cmd->i_first = i_first;
+  }
+
+  list->cmd_len++;
+  uint offset = list->cmd_offset + list->cmd_len * sizeof(GPUDrawCommandIndexed);
+
+  if (offset == list->buffer_size) {
+    GPU_draw_list_submit(list);
+    GPU_draw_list_init(list, list->batch);
+  }
+}
+
+void GPU_draw_list_submit(GPUDrawList *list)
+{
+  GPUBatch *batch = list->batch;
+
+  if (list->cmd_len == 0)
+    return;
+
+  BLI_assert(list->commands);
+  BLI_assert(batch->program_in_use);
+  /* TODO could assert that VAO is bound. */
+
+  /* TODO We loose a bit of memory here if we only draw arrays. Fix that. */
+  uintptr_t offset = list->cmd_offset;
+  uint cmd_len = list->cmd_len;
+  size_t bytes_used = cmd_len * sizeof(GPUDrawCommandIndexed);
+  list->cmd_offset += bytes_used;
+  list->cmd_len = 0; /* Avoid reuse. */
+
+  if (USE_MULTI_DRAW_INDIRECT) {
+    GLenum prim = batch->gl_prim_type;
+
+    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id);
+    glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, offset, bytes_used);
+    glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
+    list->commands = NULL; /* Unmapped */
+
+    if (batch->elem) {
+      glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch->elem), (void *)offset, cmd_len, 0);
+    }
+    else {
+      glMultiDrawArraysIndirect(prim, (void *)offset, cmd_len, 0);
+    }
+  }
+  else {
+    /* Fallback */
+    if (batch->elem) {
+      GPUDrawCommandIndexed *cmd = list->commands_indexed;
+      for (int i = 0; i < cmd_len; i++, cmd++) {
+        GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
+      }
+    }
+    else {
+      GPUDrawCommand *cmd = list->commands;
+      for (int i = 0; i < cmd_len; i++, cmd++) {
+        GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
+      }
+    }
+  }
+}
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name Utilities
  * \{ */



More information about the Bf-blender-cvs mailing list