From 38ef35b1ed1d38ab58c7d7fb121f4f3b33869810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Sat, 8 Aug 2020 15:24:52 +0200 Subject: GPUDrawList: GL backend isolation --- source/blender/draw/intern/draw_manager.h | 1 + source/blender/draw/intern/draw_manager_exec.c | 21 +- source/blender/gpu/CMakeLists.txt | 5 + source/blender/gpu/GPU_batch.h | 13 -- source/blender/gpu/GPU_drawlist.h | 46 +++++ source/blender/gpu/intern/gpu_backend.hh | 12 +- source/blender/gpu/intern/gpu_batch.cc | 187 ----------------- source/blender/gpu/intern/gpu_context.cc | 10 +- source/blender/gpu/intern/gpu_drawlist.cc | 59 ++++++ source/blender/gpu/intern/gpu_drawlist_private.hh | 40 ++++ source/blender/gpu/opengl/gl_backend.hh | 6 + source/blender/gpu/opengl/gl_drawlist.cc | 238 ++++++++++++++++++++++ source/blender/gpu/opengl/gl_drawlist.hh | 80 ++++++++ 13 files changed, 493 insertions(+), 225 deletions(-) create mode 100644 source/blender/gpu/GPU_drawlist.h create mode 100644 source/blender/gpu/intern/gpu_drawlist.cc create mode 100644 source/blender/gpu/intern/gpu_drawlist_private.hh create mode 100644 source/blender/gpu/opengl/gl_drawlist.cc create mode 100644 source/blender/gpu/opengl/gl_drawlist.hh (limited to 'source') diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 92a01cbbe04..d15a55e7bef 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -35,6 +35,7 @@ #include "GPU_batch.h" #include "GPU_context.h" +#include "GPU_drawlist.h" #include "GPU_framebuffer.h" #include "GPU_shader.h" #include "GPU_uniformbuffer.h" diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index e3860b1bfb2..8f5dba87a56 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -54,8 +54,6 @@ typedef struct DRWCommandsState { int resource_id; int base_inst; int inst_count; - int v_first; - int v_count; bool neg_scale; /* Resource location. */ int obmats_loc; @@ -714,18 +712,12 @@ BLI_INLINE void draw_indirect_call(DRWShadingGroup *shgroup, DRWCommandsState *s GPU_draw_list_submit(DST.draw_list); draw_geometry_bind(shgroup, state->batch); } - GPU_draw_list_command_add( - DST.draw_list, state->v_first, state->v_count, state->base_inst, state->inst_count); + GPU_draw_list_append(DST.draw_list, state->batch, state->base_inst, state->inst_count); } /* Fallback when unsupported */ else { - draw_geometry_execute(shgroup, - state->batch, - state->v_first, - state->v_count, - state->base_inst, - state->inst_count, - state->baseinst_loc); + draw_geometry_execute( + shgroup, state->batch, 0, 0, state->base_inst, state->inst_count, state->baseinst_loc); } } @@ -1015,8 +1007,6 @@ static void draw_call_batching_start(DRWCommandsState *state) state->resource_id = -1; state->base_inst = 0; state->inst_count = 0; - state->v_first = 0; - state->v_count = 0; state->batch = NULL; state->select_id = -1; @@ -1039,15 +1029,10 @@ static void draw_call_batching_do(DRWShadingGroup *shgroup, draw_call_batching_flush(shgroup, state); state->batch = call->batch; - state->v_first = (call->batch->elem) ? call->batch->elem->index_start : 0; - state->v_count = (call->batch->elem) ? call->batch->elem->index_len : - call->batch->verts[0]->vertex_len; state->inst_count = 1; state->base_inst = id; draw_call_resource_bind(state, &call->handle); - - GPU_draw_list_init(DST.draw_list, state->batch); } /* Is the id consecutive? */ else if (id != state->base_inst + state->inst_count) { diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 80ea28aca3c..fe5c5d4fa7b 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -63,6 +63,7 @@ set(SRC intern/gpu_codegen.c intern/gpu_context.cc intern/gpu_debug.cc + intern/gpu_drawlist.cc intern/gpu_element.cc intern/gpu_extensions.cc intern/gpu_framebuffer.cc @@ -89,6 +90,7 @@ set(SRC intern/gpu_viewport.c opengl/gl_context.cc + opengl/gl_drawlist.cc GPU_attr_binding.h GPU_batch.h @@ -98,6 +100,7 @@ set(SRC GPU_common.h GPU_context.h GPU_debug.h + GPU_drawlist.h GPU_element.h GPU_extensions.h GPU_framebuffer.h @@ -125,6 +128,7 @@ set(SRC intern/gpu_batch_private.h intern/gpu_codegen.h intern/gpu_context_private.hh + intern/gpu_drawlist_private.hh intern/gpu_material_library.h intern/gpu_matrix_private.h intern/gpu_node_graph.h @@ -136,6 +140,7 @@ set(SRC opengl/gl_backend.hh opengl/gl_context.hh + opengl/gl_drawlist.hh ) set(LIB diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 855214c279c..4b79b175fec 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -199,19 +199,6 @@ GPUBatch *create_BatchInGeneral(GPUPrimType, VertexBufferStuff, ElementListStuff #endif /* future plans */ -/** - * #GPUDrawList is an API to do lots of similar draw-calls very fast using multi-draw-indirect. - * 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/GPU_drawlist.h b/source/blender/gpu/GPU_drawlist.h new file mode 100644 index 00000000000..27f70da8cf8 --- /dev/null +++ b/source/blender/gpu/GPU_drawlist.h @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPUDrawList is an API to do lots of similar draw-calls very fast using + * multi-draw-indirect. There is a fallback if the feature is not supported. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct GPUBatch; + +typedef void *GPUDrawList; /* Opaque pointer. */ + +/* Create a list with at least length drawcalls. Length can affect performance. */ +GPUDrawList GPU_draw_list_create(int length); +void GPU_draw_list_discard(GPUDrawList list); + +void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count); +void GPU_draw_list_submit(GPUDrawList list); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 24f592f214f..4dd6036e672 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -25,13 +25,21 @@ #pragma once -struct GPUContext; +#include "gpu_context_private.hh" +#include "gpu_drawlist_private.hh" + +namespace blender { +namespace gpu { class GPUBackend { public: virtual ~GPUBackend(){}; + static GPUBackend *get(void); + virtual GPUContext *context_alloc(void *ghost_window) = 0; + virtual DrawList *drawlist_alloc(int list_length) = 0; }; -GPUBackend *gpu_backend_get(void); +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index a6ba4d3d89a..5b8a3c4636b 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -795,193 +795,6 @@ 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 && GPU_arb_base_instance_is_supported()) -#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 = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); - /* Alloc the biggest possible command list which is indexed. */ - list->buffer_size = sizeof(GPUDrawCommandIndexed) * length; - 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 = (GPUDrawCommand *)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 = (GPUDrawCommand *)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 (v_count == 0 || i_count == 0) { - return; - } - - 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_len = 0; /* Avoid reuse. */ - - /* Only do multi-draw indirect if doing more than 2 drawcall. - * This avoids the overhead of buffer mapping if scene is - * not very instance friendly. - * BUT we also need to take into account the case where only - * a few instances are needed to finish filling a call buffer. */ - const bool do_mdi = (cmd_len > 2) || (list->cmd_offset + bytes_used == list->buffer_size); - - if (USE_MULTI_DRAW_INDIRECT && do_mdi) { - GLenum prim = batch->gl_prim_type; - - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id); - glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, bytes_used); - glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER); - list->commands = NULL; /* Unmapped */ - list->cmd_offset += bytes_used; - - 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++) { - /* Index start was added by Draw manager. Avoid counting it twice. */ - cmd->v_first -= batch->elem->index_start; - 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 * \{ */ diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 283784aec20..da0d3cd395f 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -83,12 +83,12 @@ bool GPUContext::is_active_on_thread(void) GPUContext *GPU_context_create(void *ghost_window) { - if (gpu_backend_get() == NULL) { + if (GPUBackend::get() == NULL) { /* TODO move where it make sense. */ GPU_backend_init(GPU_BACKEND_OPENGL); } - GPUContext *ctx = gpu_backend_get()->context_alloc(ghost_window); + GPUContext *ctx = GPUBackend::get()->context_alloc(ghost_window); GPU_context_active_set(ctx); return ctx; @@ -173,14 +173,14 @@ void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx) void GPU_buf_free(GLuint buf_id) { /* TODO avoid using backend */ - GPUBackend *backend = gpu_backend_get(); + GPUBackend *backend = GPUBackend::get(); static_cast(backend)->buf_free(buf_id); } void GPU_tex_free(GLuint tex_id) { /* TODO avoid using backend */ - GPUBackend *backend = gpu_backend_get(); + GPUBackend *backend = GPUBackend::get(); static_cast(backend)->tex_free(tex_id); } @@ -285,7 +285,7 @@ void GPU_backend_exit(void) delete g_backend; } -GPUBackend *gpu_backend_get(void) +GPUBackend *GPUBackend::get(void) { return g_backend; } diff --git a/source/blender/gpu/intern/gpu_drawlist.cc b/source/blender/gpu/intern/gpu_drawlist.cc new file mode 100644 index 00000000000..7b807a2fa80 --- /dev/null +++ b/source/blender/gpu/intern/gpu_drawlist.cc @@ -0,0 +1,59 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Implementation of Multi Draw Indirect. + */ + +#include "MEM_guardedalloc.h" + +#include "GPU_batch.h" +#include "GPU_drawlist.h" + +#include "gpu_backend.hh" + +#include "gpu_drawlist_private.hh" + +using namespace blender::gpu; + +GPUDrawList GPU_draw_list_create(int list_length) +{ + DrawList *list_ptr = GPUBackend::get()->drawlist_alloc(list_length); + return reinterpret_cast(list_ptr); +} + +void GPU_draw_list_discard(GPUDrawList list) +{ + DrawList *list_ptr = reinterpret_cast(list); + delete list_ptr; +} + +void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count) +{ + DrawList *list_ptr = reinterpret_cast(list); + list_ptr->append(batch, i_first, i_count); +} + +void GPU_draw_list_submit(GPUDrawList list) +{ + DrawList *list_ptr = reinterpret_cast(list); + list_ptr->submit(); +} diff --git a/source/blender/gpu/intern/gpu_drawlist_private.hh b/source/blender/gpu/intern/gpu_drawlist_private.hh new file mode 100644 index 00000000000..04cc18a5ffd --- /dev/null +++ b/source/blender/gpu/intern/gpu_drawlist_private.hh @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +namespace blender { +namespace gpu { + +class DrawList { + public: + virtual ~DrawList(){}; + + virtual void append(GPUBatch *batch, int i_first, int i_count) = 0; + virtual void submit() = 0; +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index f7c01b2f184..a1e0e53f329 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -28,6 +28,7 @@ #include "BLI_vector.hh" #include "gl_context.hh" +#include "gl_drawlist.hh" namespace blender { namespace gpu { @@ -42,6 +43,11 @@ class GLBackend : public GPUBackend { return new GLContext(ghost_window, shared_orphan_list_); }; + DrawList *drawlist_alloc(int list_length) + { + return new GLDrawList(list_length); + }; + /* TODO remove */ void buf_free(GLuint buf_id); void tex_free(GLuint tex_id); diff --git a/source/blender/gpu/opengl/gl_drawlist.cc b/source/blender/gpu/opengl/gl_drawlist.cc new file mode 100644 index 00000000000..3fc6985e582 --- /dev/null +++ b/source/blender/gpu/opengl/gl_drawlist.cc @@ -0,0 +1,238 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Implementation of Multi Draw Indirect using OpenGL. + * Fallback if the needed extensions are not supported. + */ + +#include "BLI_assert.h" + +#include "GPU_batch.h" +#include "GPU_extensions.h" + +#include "glew-mx.h" + +#include "gpu_context_private.hh" +#include "gpu_drawlist_private.hh" + +#include "gl_backend.hh" +#include "gl_drawlist.hh" + +#include + +#define USE_MULTI_DRAW_INDIRECT 1 + +/* TODO remove. */ +#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 + +using namespace blender::gpu; + +typedef struct GLDrawCommand { + GLuint v_count; + GLuint i_count; + GLuint v_first; + GLuint i_first; +} GLDrawCommand; + +typedef struct GLDrawCommandIndexed { + GLuint v_count; + GLuint i_count; + GLuint v_first; + GLuint base_index; + GLuint i_first; +} GLDrawCommandIndexed; + +#define MDI_ENABLED (buffer_size_ != 0) +#define MDI_DISABLED (buffer_size_ == 0) +#define MDI_INDEXED (base_index_ != UINT_MAX) + +GLDrawList::GLDrawList(int length) +{ + BLI_assert(length > 0); + batch_ = NULL; + buffer_id_ = 0; + command_len_ = 0; + command_offset_ = 0; + data_offset_ = 0; + data_size_ = 0; + data_ = NULL; + + if (USE_MULTI_DRAW_INDIRECT && GLEW_ARB_multi_draw_indirect && + GPU_arb_base_instance_is_supported()) { + /* Alloc the biggest possible command list, which is indexed. */ + buffer_size_ = sizeof(GLDrawCommandIndexed) * length; + } + else { + /* Indicates MDI is not supported. */ + buffer_size_ = 0; + } +} + +GLDrawList::~GLDrawList() +{ + /* TODO This ... */ + static_cast(GPUBackend::get())->buf_free(buffer_id_); + /* ... should be this. */ + // context_->buf_free(buffer_id_) +} + +void GLDrawList::init(void) +{ + BLI_assert(GPU_context_active_get()); + BLI_assert(MDI_ENABLED); + BLI_assert(data_ == NULL); + batch_ = NULL; + command_len_ = 0; + + if (buffer_id_ == 0) { + /* Allocate on first use. */ + glGenBuffers(1, &buffer_id_); + context_ = static_cast(GPU_context_active_get()); + } + + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_); + /* If buffer is full, orphan buffer data and start fresh. */ + // if (command_offset_ >= data_size_) { + glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, NULL, GL_DYNAMIC_DRAW); + data_offset_ = 0; + // } + /* Map the remaining range. */ + GLbitfield flag = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + data_size_ = buffer_size_ - data_offset_; + data_ = (GLbyte *)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, data_offset_, data_size_, flag); + command_offset_ = 0; +} + +void GLDrawList::append(GPUBatch *batch, int i_first, int i_count) +{ + /* Fallback when MultiDrawIndirect is not supported/enabled. */ + if (MDI_DISABLED) { + GPU_batch_draw_advanced(batch, 0, 0, i_first, i_count); + return; + } + + if (data_ == NULL) { + this->init(); + } + + if (batch != batch_) { + // BLI_assert(batch->flag | GPU_BATCH_INIT); + this->submit(); + batch_ = batch; + /* Cached for faster access. */ + base_index_ = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX; + v_first_ = batch->elem ? batch->elem->index_start : 0; + v_count_ = batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len; + } + + if (MDI_INDEXED) { + GLDrawCommandIndexed *cmd = reinterpret_cast(data_ + command_offset_); + cmd->v_first = v_first_; + cmd->v_count = v_count_; + cmd->i_count = i_count; + cmd->base_index = base_index_; + cmd->i_first = i_first; + command_offset_ += sizeof(GLDrawCommandIndexed); + } + else { + GLDrawCommand *cmd = reinterpret_cast(data_ + command_offset_); + cmd->v_first = v_first_; + cmd->v_count = v_count_; + cmd->i_count = i_count; + cmd->i_first = i_first; + command_offset_ += sizeof(GLDrawCommand); + } + + command_len_++; + + if (command_offset_ >= data_size_) { + this->submit(); + } +} + +void GLDrawList::submit(void) +{ + if (command_len_ == 0) { + return; + } + /* Something's wrong if we get here without MDI support. */ + BLI_assert(MDI_ENABLED); + BLI_assert(data_); + /* TODO fix this assert */ + // BLI_assert(batch_->program_in_use); + /* TODO could assert that VAO is bound. */ + + /* Only do multi-draw indirect if doing more than 2 drawcall. This avoids the overhead of + * buffer mapping if scene is not very instance friendly. BUT we also need to take into + * account the + * case where only a few instances are needed to finish filling a call buffer. */ + const bool is_finishing_a_buffer = (command_offset_ >= data_size_); + if (command_len_ > 2 || is_finishing_a_buffer) { + // GLenum prim = convert_prim_type_to_gl(batch_->prim_type); + GLenum prim = batch_->gl_prim_type; + void *offset = (void *)data_offset_; + + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_); + glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, command_offset_); + glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER); + data_ = NULL; /* Unmapped */ + data_offset_ += command_offset_; + + if (MDI_INDEXED) { + glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch_->elem), offset, command_len_, 0); + } + else { + glMultiDrawArraysIndirect(prim, offset, command_len_, 0); + } + } + else { + /* Fallback do simple drawcalls, and don't unmap the buffer. */ + if (MDI_INDEXED) { + GLDrawCommandIndexed *cmd = (GLDrawCommandIndexed *)data_; + for (int i = 0; i < command_len_; i++, cmd++) { + /* Index start was already added. Avoid counting it twice. */ + cmd->v_first -= batch_->elem->index_start; + GPU_batch_draw_advanced(batch_, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); + } + /* Reuse the same data. */ + command_offset_ -= command_len_ * sizeof(GLDrawCommandIndexed); + } + else { + GLDrawCommand *cmd = (GLDrawCommand *)data_; + for (int i = 0; i < command_len_; i++, cmd++) { + GPU_batch_draw_advanced(batch_, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); + } + /* Reuse the same data. */ + command_offset_ -= command_len_ * sizeof(GLDrawCommand); + } + } + /* Do not submit this buffer again. */ + command_len_ = 0; +} + +/** \} */ \ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_drawlist.hh b/source/blender/gpu/opengl/gl_drawlist.hh new file mode 100644 index 00000000000..4f085149388 --- /dev/null +++ b/source/blender/gpu/opengl/gl_drawlist.hh @@ -0,0 +1,80 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "BLI_sys_types.h" + +#include "GPU_batch.h" +#include "GPU_glew.h" + +#include "gpu_drawlist_private.hh" + +#include "gl_context.hh" + +namespace blender { +namespace gpu { + +class GLDrawList : public DrawList { + public: + GLDrawList(int length); + ~GLDrawList(); + + void append(GPUBatch *batch, int i_first, int i_count) override; + void submit(void) override; + + private: + void init(void); + + /** Batch for which we are recording commands for. */ + GPUBatch *batch_; + /** Mapped memory bounds. */ + GLbyte *data_; + /** Length of the mapped buffer (in byte). */ + GLsizeiptr data_size_; + /** Current offset inside the mapped buffer (in byte). */ + GLintptr command_offset_; + /** Current number of command recorded inside the mapped buffer. */ + uint command_len_; + /** Is UINT_MAX if not drawing indexed geom. Also Avoid dereferencing batch. */ + GLuint base_index_; + /** Also Avoid dereferencing batch. */ + GLuint v_first_, v_count_; + + /** GL Indirect Buffer id. 0 means MultiDrawIndirect is not supported/enabled. */ + GLuint buffer_id_; + /** Length of whole the buffer (in byte). */ + GLsizeiptr buffer_size_; + /** Offset of data_ inside the whole buffer (in byte). */ + GLintptr data_offset_; + + /** To free the buffer_id_. */ + GLContext *context_; + + MEM_CXX_CLASS_ALLOC_FUNCS("GLDrawList"); +}; + +} // namespace gpu +} // namespace blender -- cgit v1.2.3