| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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"
 | 
					
						
							| 
									
										
										
										
											2020-09-07 19:35:56 +02:00
										 |  |  | #include "GPU_capabilities.h"
 | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "glew-mx.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "gpu_context_private.hh"
 | 
					
						
							|  |  |  | #include "gpu_drawlist_private.hh"
 | 
					
						
							| 
									
										
										
										
											2020-09-06 16:40:07 +02:00
										 |  |  | #include "gpu_vertex_buffer_private.hh"
 | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "gl_backend.hh"
 | 
					
						
							|  |  |  | #include "gl_drawlist.hh"
 | 
					
						
							| 
									
										
										
										
											2020-08-31 15:09:15 +02:00
										 |  |  | #include "gl_primitive.hh"
 | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-04 11:28:09 +01:00
										 |  |  | #include <climits>
 | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | using namespace blender::gpu; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-04 12:46:43 +01:00
										 |  |  | struct GLDrawCommand { | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   GLuint v_count; | 
					
						
							|  |  |  |   GLuint i_count; | 
					
						
							|  |  |  |   GLuint v_first; | 
					
						
							|  |  |  |   GLuint i_first; | 
					
						
							| 
									
										
										
										
											2020-12-04 12:46:43 +01:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-04 12:46:43 +01:00
										 |  |  | struct GLDrawCommandIndexed { | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   GLuint v_count; | 
					
						
							|  |  |  |   GLuint i_count; | 
					
						
							|  |  |  |   GLuint v_first; | 
					
						
							|  |  |  |   GLuint base_index; | 
					
						
							|  |  |  |   GLuint i_first; | 
					
						
							| 
									
										
										
										
											2020-12-04 12:46:43 +01:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #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); | 
					
						
							| 
									
										
										
										
											2020-11-06 17:49:09 +01:00
										 |  |  |   batch_ = nullptr; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   buffer_id_ = 0; | 
					
						
							|  |  |  |   command_len_ = 0; | 
					
						
							| 
									
										
										
										
											2021-04-30 12:31:52 +10:00
										 |  |  |   base_index_ = 0; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   command_offset_ = 0; | 
					
						
							|  |  |  |   data_size_ = 0; | 
					
						
							| 
									
										
										
										
											2020-11-06 17:49:09 +01:00
										 |  |  |   data_ = nullptr; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-10 14:18:19 +02:00
										 |  |  |   if (GLContext::multi_draw_indirect_support) { | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |     /* Alloc the biggest possible command list, which is indexed. */ | 
					
						
							|  |  |  |     buffer_size_ = sizeof(GLDrawCommandIndexed) * length; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     /* Indicates MDI is not supported. */ | 
					
						
							|  |  |  |     buffer_size_ = 0; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-02-01 13:40:57 +01:00
										 |  |  |   /* Force buffer specification on first init. */ | 
					
						
							|  |  |  |   data_offset_ = buffer_size_; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | GLDrawList::~GLDrawList() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-09-07 20:08:25 +02:00
										 |  |  |   GLContext::buf_free(buffer_id_); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-06 13:18:48 +01:00
										 |  |  | void GLDrawList::init() | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-09-08 04:12:12 +02:00
										 |  |  |   BLI_assert(GLContext::get()); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   BLI_assert(MDI_ENABLED); | 
					
						
							| 
									
										
										
										
											2020-11-06 17:49:09 +01:00
										 |  |  |   BLI_assert(data_ == nullptr); | 
					
						
							|  |  |  |   batch_ = nullptr; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   command_len_ = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (buffer_id_ == 0) { | 
					
						
							|  |  |  |     /* Allocate on first use. */ | 
					
						
							|  |  |  |     glGenBuffers(1, &buffer_id_); | 
					
						
							| 
									
										
										
										
											2020-09-08 04:12:12 +02:00
										 |  |  |     context_ = GLContext::get(); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_); | 
					
						
							|  |  |  |   /* If buffer is full, orphan buffer data and start fresh. */ | 
					
						
							| 
									
										
										
										
											2021-02-03 00:15:05 +01:00
										 |  |  |   size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand); | 
					
						
							|  |  |  |   if (data_offset_ + command_size > buffer_size_) { | 
					
						
							| 
									
										
										
										
											2021-02-01 13:40:57 +01:00
										 |  |  |     glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, nullptr, GL_DYNAMIC_DRAW); | 
					
						
							|  |  |  |     data_offset_ = 0; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   /* 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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  | void GLDrawList::append(GPUBatch *gpu_batch, int i_first, int i_count) | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   /* Fallback when MultiDrawIndirect is not supported/enabled. */ | 
					
						
							|  |  |  |   if (MDI_DISABLED) { | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  |     GPU_batch_draw_advanced(gpu_batch, 0, 0, i_first, i_count); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-06 17:49:09 +01:00
										 |  |  |   if (data_ == nullptr) { | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |     this->init(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  |   GLBatch *batch = static_cast<GLBatch *>(gpu_batch); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   if (batch != batch_) { | 
					
						
							|  |  |  |     // BLI_assert(batch->flag | GPU_BATCH_INIT);
 | 
					
						
							|  |  |  |     this->submit(); | 
					
						
							|  |  |  |     batch_ = batch; | 
					
						
							|  |  |  |     /* Cached for faster access. */ | 
					
						
							| 
									
										
										
										
											2020-09-06 23:45:51 +02:00
										 |  |  |     GLIndexBuf *el = batch_->elem_(); | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  |     base_index_ = el ? el->index_base_ : UINT_MAX; | 
					
						
							|  |  |  |     v_first_ = el ? el->index_start_ : 0; | 
					
						
							| 
									
										
										
										
											2020-09-06 23:45:51 +02:00
										 |  |  |     v_count_ = el ? el->index_len_ : batch->verts_(0)->vertex_len; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 13:53:00 +02:00
										 |  |  |   if (v_count_ == 0) { | 
					
						
							|  |  |  |     /* Nothing to draw. */ | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   if (MDI_INDEXED) { | 
					
						
							|  |  |  |     GLDrawCommandIndexed *cmd = reinterpret_cast<GLDrawCommandIndexed *>(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; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     GLDrawCommand *cmd = reinterpret_cast<GLDrawCommand *>(data_ + command_offset_); | 
					
						
							|  |  |  |     cmd->v_first = v_first_; | 
					
						
							|  |  |  |     cmd->v_count = v_count_; | 
					
						
							|  |  |  |     cmd->i_count = i_count; | 
					
						
							|  |  |  |     cmd->i_first = i_first; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-03 00:15:05 +01:00
										 |  |  |   size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   command_offset_ += command_size; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   command_len_++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-03 00:15:05 +01:00
										 |  |  |   /* Check if we can fit at least one other command. */ | 
					
						
							|  |  |  |   if (command_offset_ + command_size > data_size_) { | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |     this->submit(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-06 13:18:48 +01:00
										 |  |  | void GLDrawList::submit() | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   if (command_len_ == 0) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* Something's wrong if we get here without MDI support. */ | 
					
						
							|  |  |  |   BLI_assert(MDI_ENABLED); | 
					
						
							|  |  |  |   BLI_assert(data_); | 
					
						
							| 
									
										
										
										
											2020-11-06 17:49:09 +01:00
										 |  |  |   BLI_assert(GLContext::get()->shader != nullptr); | 
					
						
							| 
									
										
										
										
											2020-08-13 14:17:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-03 00:15:05 +01:00
										 |  |  |   size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   /* 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 | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  |    * account the case where only a few instances are needed to finish filling a call buffer. */ | 
					
						
							| 
									
										
										
										
											2021-02-03 00:15:05 +01:00
										 |  |  |   const bool is_finishing_a_buffer = (command_offset_ + command_size > data_size_); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |   if (command_len_ > 2 || is_finishing_a_buffer) { | 
					
						
							| 
									
										
										
										
											2020-08-31 15:09:15 +02:00
										 |  |  |     GLenum prim = to_gl(batch_->prim_type); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |     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); | 
					
						
							| 
									
										
										
										
											2020-11-06 17:49:09 +01:00
										 |  |  |     data_ = nullptr; /* Unmapped */ | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |     data_offset_ += command_offset_; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  |     batch_->bind(0); | 
					
						
							| 
									
										
										
										
											2020-08-13 02:29:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |     if (MDI_INDEXED) { | 
					
						
							| 
									
										
										
										
											2020-09-06 23:45:51 +02:00
										 |  |  |       GLenum gl_type = to_gl(batch_->elem_()->index_type_); | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  |       glMultiDrawElementsIndirect(prim, gl_type, offset, command_len_, 0); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     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. */ | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  |         cmd->v_first -= v_first_; | 
					
						
							|  |  |  |         batch_->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |       /* Reuse the same data. */ | 
					
						
							|  |  |  |       command_offset_ -= command_len_ * sizeof(GLDrawCommandIndexed); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       GLDrawCommand *cmd = (GLDrawCommand *)data_; | 
					
						
							|  |  |  |       for (int i = 0; i < command_len_; i++, cmd++) { | 
					
						
							| 
									
										
										
										
											2020-09-06 02:46:51 +02:00
										 |  |  |         batch_->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |       /* Reuse the same data. */ | 
					
						
							|  |  |  |       command_offset_ -= command_len_ * sizeof(GLDrawCommand); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* Do not submit this buffer again. */ | 
					
						
							|  |  |  |   command_len_ = 0; | 
					
						
							| 
									
										
										
										
											2020-08-18 13:52:20 +02:00
										 |  |  |   /* Avoid keeping reference to the batch. */ | 
					
						
							| 
									
										
										
										
											2020-11-06 17:49:09 +01:00
										 |  |  |   batch_ = nullptr; | 
					
						
							| 
									
										
										
										
											2020-08-08 15:24:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-17 12:39:08 +10:00
										 |  |  | /** \} */ |