Blender  V2.93
gl_drawlist.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2016 by Mike Erwin.
17  * All rights reserved.
18  */
19 
27 #include "BLI_assert.h"
28 
29 #include "GPU_batch.h"
30 #include "GPU_capabilities.h"
31 
32 #include "glew-mx.h"
33 
34 #include "gpu_context_private.hh"
35 #include "gpu_drawlist_private.hh"
37 
38 #include "gl_backend.hh"
39 #include "gl_drawlist.hh"
40 #include "gl_primitive.hh"
41 
42 #include <climits>
43 
44 using namespace blender::gpu;
45 
46 struct GLDrawCommand {
47  GLuint v_count;
48  GLuint i_count;
49  GLuint v_first;
50  GLuint i_first;
51 };
52 
54  GLuint v_count;
55  GLuint i_count;
56  GLuint v_first;
57  GLuint base_index;
58  GLuint i_first;
59 };
60 
61 #define MDI_ENABLED (buffer_size_ != 0)
62 #define MDI_DISABLED (buffer_size_ == 0)
63 #define MDI_INDEXED (base_index_ != UINT_MAX)
64 
66 {
67  BLI_assert(length > 0);
68  batch_ = nullptr;
69  buffer_id_ = 0;
70  command_len_ = 0;
71  base_index_ = 0;
72  command_offset_ = 0;
73  data_size_ = 0;
74  data_ = nullptr;
75 
77  /* Alloc the biggest possible command list, which is indexed. */
78  buffer_size_ = sizeof(GLDrawCommandIndexed) * length;
79  }
80  else {
81  /* Indicates MDI is not supported. */
82  buffer_size_ = 0;
83  }
84  /* Force buffer specification on first init. */
85  data_offset_ = buffer_size_;
86 }
87 
89 {
90  GLContext::buf_free(buffer_id_);
91 }
92 
93 void GLDrawList::init()
94 {
97  BLI_assert(data_ == nullptr);
98  batch_ = nullptr;
99  command_len_ = 0;
100 
101  if (buffer_id_ == 0) {
102  /* Allocate on first use. */
103  glGenBuffers(1, &buffer_id_);
104  context_ = GLContext::get();
105  }
106 
107  glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_);
108  /* If buffer is full, orphan buffer data and start fresh. */
109  size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand);
110  if (data_offset_ + command_size > buffer_size_) {
111  glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, nullptr, GL_DYNAMIC_DRAW);
112  data_offset_ = 0;
113  }
114  /* Map the remaining range. */
115  GLbitfield flag = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
116  data_size_ = buffer_size_ - data_offset_;
117  data_ = (GLbyte *)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, data_offset_, data_size_, flag);
118  command_offset_ = 0;
119 }
120 
121 void GLDrawList::append(GPUBatch *gpu_batch, int i_first, int i_count)
122 {
123  /* Fallback when MultiDrawIndirect is not supported/enabled. */
124  if (MDI_DISABLED) {
125  GPU_batch_draw_advanced(gpu_batch, 0, 0, i_first, i_count);
126  return;
127  }
128 
129  if (data_ == nullptr) {
130  this->init();
131  }
132 
133  GLBatch *batch = static_cast<GLBatch *>(gpu_batch);
134  if (batch != batch_) {
135  // BLI_assert(batch->flag | GPU_BATCH_INIT);
136  this->submit();
137  batch_ = batch;
138  /* Cached for faster access. */
139  GLIndexBuf *el = batch_->elem_();
140  base_index_ = el ? el->index_base_ : UINT_MAX;
141  v_first_ = el ? el->index_start_ : 0;
142  v_count_ = el ? el->index_len_ : batch->verts_(0)->vertex_len;
143  }
144 
145  if (v_count_ == 0) {
146  /* Nothing to draw. */
147  return;
148  }
149 
150  if (MDI_INDEXED) {
151  GLDrawCommandIndexed *cmd = reinterpret_cast<GLDrawCommandIndexed *>(data_ + command_offset_);
152  cmd->v_first = v_first_;
153  cmd->v_count = v_count_;
154  cmd->i_count = i_count;
155  cmd->base_index = base_index_;
156  cmd->i_first = i_first;
157  }
158  else {
159  GLDrawCommand *cmd = reinterpret_cast<GLDrawCommand *>(data_ + command_offset_);
160  cmd->v_first = v_first_;
161  cmd->v_count = v_count_;
162  cmd->i_count = i_count;
163  cmd->i_first = i_first;
164  }
165 
166  size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand);
167 
168  command_offset_ += command_size;
169  command_len_++;
170 
171  /* Check if we can fit at least one other command. */
172  if (command_offset_ + command_size > data_size_) {
173  this->submit();
174  }
175 }
176 
178 {
179  if (command_len_ == 0) {
180  return;
181  }
182  /* Something's wrong if we get here without MDI support. */
184  BLI_assert(data_);
185  BLI_assert(GLContext::get()->shader != nullptr);
186 
187  size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand);
188 
189  /* Only do multi-draw indirect if doing more than 2 drawcall. This avoids the overhead of
190  * buffer mapping if scene is not very instance friendly. BUT we also need to take into
191  * account the case where only a few instances are needed to finish filling a call buffer. */
192  const bool is_finishing_a_buffer = (command_offset_ + command_size > data_size_);
193  if (command_len_ > 2 || is_finishing_a_buffer) {
194  GLenum prim = to_gl(batch_->prim_type);
195  void *offset = (void *)data_offset_;
196 
197  glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_);
198  glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, command_offset_);
199  glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
200  data_ = nullptr; /* Unmapped */
201  data_offset_ += command_offset_;
202 
203  batch_->bind(0);
204 
205  if (MDI_INDEXED) {
206  GLenum gl_type = to_gl(batch_->elem_()->index_type_);
207  glMultiDrawElementsIndirect(prim, gl_type, offset, command_len_, 0);
208  }
209  else {
210  glMultiDrawArraysIndirect(prim, offset, command_len_, 0);
211  }
212  }
213  else {
214  /* Fallback do simple drawcalls, and don't unmap the buffer. */
215  if (MDI_INDEXED) {
217  for (int i = 0; i < command_len_; i++, cmd++) {
218  /* Index start was already added. Avoid counting it twice. */
219  cmd->v_first -= v_first_;
220  batch_->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
221  }
222  /* Reuse the same data. */
223  command_offset_ -= command_len_ * sizeof(GLDrawCommandIndexed);
224  }
225  else {
226  GLDrawCommand *cmd = (GLDrawCommand *)data_;
227  for (int i = 0; i < command_len_; i++, cmd++) {
228  batch_->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
229  }
230  /* Reuse the same data. */
231  command_offset_ -= command_len_ * sizeof(GLDrawCommand);
232  }
233  }
234  /* Do not submit this buffer again. */
235  command_len_ = 0;
236  /* Avoid keeping reference to the batch. */
237  batch_ = nullptr;
238 }
239 
#define BLI_assert(a)
Definition: BLI_assert.h:58
GPUBatch
Definition: GPU_batch.h:93
void GPU_batch_draw_advanced(GPUBatch *, int v_first, int v_count, int i_first, int i_count)
Definition: gpu_batch.cc:255
SIMD_FORCE_INLINE btScalar length(const btQuaternion &q)
Return the length of a quaternion.
Definition: btQuaternion.h:895
void bind(int i_first)
Definition: gl_batch.cc:285
void draw(int v_first, int v_count, int i_first, int i_count) override
Definition: gl_batch.cc:310
GLIndexBuf * elem_(void) const
Definition: gl_batch.hh:103
static void buf_free(GLuint buf_id)
Definition: gl_context.cc:257
static GLContext * get()
Definition: gl_context.hh:118
static bool multi_draw_indirect_support
Definition: gl_context.hh:71
void submit(void) override
Definition: gl_drawlist.cc:177
void append(GPUBatch *batch, int i_first, int i_count) override
Definition: gl_drawlist.cc:121
GPUBatch * batch
Definition: drawnode.c:3779
#define MDI_INDEXED
Definition: gl_drawlist.cc:63
#define MDI_ENABLED
Definition: gl_drawlist.cc:61
#define MDI_DISABLED
Definition: gl_drawlist.cc:62
#define UINT_MAX
Definition: hash_md5.c:58
void KERNEL_FUNCTION_FULL_NAME() shader(KernelGlobals *kg, uint4 *input, float4 *output, int type, int filter, int i, int offset, int sample)
static GLenum to_gl(const GPUAttachmentType type)
GLuint v_first
Definition: gl_drawlist.cc:49
GLuint i_first
Definition: gl_drawlist.cc:50
GLuint v_count
Definition: gl_drawlist.cc:47
GLuint i_count
Definition: gl_drawlist.cc:48