Blender  V2.93
gl_batch.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 
28 #include "BLI_assert.h"
29 
30 #include "glew-mx.h"
31 
32 #include "gpu_batch_private.hh"
33 #include "gpu_shader_private.hh"
34 
35 #include "gl_backend.hh"
36 #include "gl_context.hh"
37 #include "gl_debug.hh"
38 #include "gl_index_buffer.hh"
39 #include "gl_primitive.hh"
40 #include "gl_vertex_array.hh"
41 
42 #include "gl_batch.hh"
43 
44 using namespace blender::gpu;
45 
46 /* -------------------------------------------------------------------- */
54 {
55  init();
56 }
57 
59 {
60  this->clear();
61 }
62 
63 void GLVaoCache::init()
64 {
65  context_ = nullptr;
66  interface_ = nullptr;
67  is_dynamic_vao_count = false;
68  for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
69  static_vaos.interfaces[i] = nullptr;
70  static_vaos.vao_ids[i] = 0;
71  }
72  vao_base_instance_ = 0;
73  base_instance_ = 0;
74  vao_id_ = 0;
75 }
76 
77 /* Create a new VAO object and store it in the cache. */
78 void GLVaoCache::insert(const GLShaderInterface *interface, GLuint vao)
79 {
80  /* Now insert the cache. */
81  if (!is_dynamic_vao_count) {
82  int i; /* find first unused slot */
83  for (i = 0; i < GPU_VAO_STATIC_LEN; i++) {
84  if (static_vaos.vao_ids[i] == 0) {
85  break;
86  }
87  }
88 
89  if (i < GPU_VAO_STATIC_LEN) {
90  static_vaos.interfaces[i] = interface;
91  static_vaos.vao_ids[i] = vao;
92  }
93  else {
94  /* Erase previous entries, they will be added back if drawn again. */
95  for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
96  if (static_vaos.interfaces[i] != nullptr) {
97  const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this);
98  context_->vao_free(static_vaos.vao_ids[i]);
99  }
100  }
101  /* Not enough place switch to dynamic. */
102  is_dynamic_vao_count = true;
103  /* Init dynamic arrays and let the branch below set the values. */
105  dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_callocN(
106  dynamic_vaos.count * sizeof(GLShaderInterface *), "dyn vaos interfaces");
107  dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(dynamic_vaos.count * sizeof(GLuint),
108  "dyn vaos ids");
109  }
110  }
111 
112  if (is_dynamic_vao_count) {
113  int i; /* find first unused slot */
114  for (i = 0; i < dynamic_vaos.count; i++) {
115  if (dynamic_vaos.vao_ids[i] == 0) {
116  break;
117  }
118  }
119 
120  if (i == dynamic_vaos.count) {
121  /* Not enough place, realloc the array. */
122  i = dynamic_vaos.count;
124  dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_recallocN(
125  (void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * dynamic_vaos.count);
126  dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(dynamic_vaos.vao_ids,
127  sizeof(GLuint) * dynamic_vaos.count);
128  }
129  dynamic_vaos.interfaces[i] = interface;
130  dynamic_vaos.vao_ids[i] = vao;
131  }
132 
133  const_cast<GLShaderInterface *>(interface)->ref_add(this);
134 }
135 
136 void GLVaoCache::remove(const GLShaderInterface *interface)
137 {
138  const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
139  GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
140  const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
141  static_vaos.interfaces;
142  for (int i = 0; i < count; i++) {
143  if (interfaces[i] == interface) {
144  context_->vao_free(vaos[i]);
145  vaos[i] = 0;
146  interfaces[i] = nullptr;
147  break; /* cannot have duplicates */
148  }
149  }
150 }
151 
153 {
154  GLContext *ctx = GLContext::get();
155  const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
156  GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
157  const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
158  static_vaos.interfaces;
159  /* Early out, nothing to free. */
160  if (context_ == nullptr) {
161  return;
162  }
163 
164  if (context_ == ctx) {
165  glDeleteVertexArrays(count, vaos);
166  glDeleteVertexArrays(1, &vao_base_instance_);
167  }
168  else {
169  /* TODO(fclem): Slow way. Could avoid multiple mutex lock here */
170  for (int i = 0; i < count; i++) {
171  context_->vao_free(vaos[i]);
172  }
173  context_->vao_free(vao_base_instance_);
174  }
175 
176  for (int i = 0; i < count; i++) {
177  if (interfaces[i] != nullptr) {
178  const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this);
179  }
180  }
181 
182  if (is_dynamic_vao_count) {
183  MEM_freeN((void *)dynamic_vaos.interfaces);
184  MEM_freeN(dynamic_vaos.vao_ids);
185  }
186 
187  if (context_) {
188  context_->vao_cache_unregister(this);
189  }
190  /* Reinit. */
191  this->init();
192 }
193 
194 /* Return 0 on cache miss (invalid VAO) */
195 GLuint GLVaoCache::lookup(const GLShaderInterface *interface)
196 {
197  const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
198  const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
199  static_vaos.interfaces;
200  for (int i = 0; i < count; i++) {
201  if (interfaces[i] == interface) {
202  return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i];
203  }
204  }
205  return 0;
206 }
207 
208 /* The GLVaoCache object is only valid for one GLContext.
209  * Reset the cache if trying to draw in another context; */
210 void GLVaoCache::context_check()
211 {
212  GLContext *ctx = GLContext::get();
213  BLI_assert(ctx);
214 
215  if (context_ != ctx) {
216  if (context_ != nullptr) {
217  /* IMPORTANT: Trying to draw a batch in multiple different context will trash the VAO cache.
218  * This has major performance impact and should be avoided in most cases. */
219  context_->vao_cache_unregister(this);
220  }
221  this->clear();
222  context_ = ctx;
223  context_->vao_cache_register(this);
224  }
225 }
226 
228 {
229  this->context_check();
230  /* Make sure the interface is up to date. */
232  GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
233  if (interface_ != interface) {
234  vao_get(batch);
235  /* Trigger update. */
236  base_instance_ = 0;
237  }
242 #ifdef __APPLE__
243  glDeleteVertexArrays(1, &vao_base_instance_);
244  vao_base_instance_ = 0;
245  base_instance_ = 0;
246 #endif
247 
248  if (vao_base_instance_ == 0) {
249  glGenVertexArrays(1, &vao_base_instance_);
250  }
251 
252  if (base_instance_ != i_first) {
253  base_instance_ = i_first;
254  GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first);
255  }
256  return vao_base_instance_;
257 }
258 
260 {
261  this->context_check();
262 
264  GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
265  if (interface_ != interface) {
266  interface_ = interface;
267  vao_id_ = this->lookup(interface_);
268 
269  if (vao_id_ == 0) {
270  /* Cache miss, create a new VAO. */
271  glGenVertexArrays(1, &vao_id_);
272  this->insert(interface_, vao_id_);
273  GLVertArray::update_bindings(vao_id_, batch, interface_, 0);
274  }
275  }
276 
277  return vao_id_;
278 }
281 /* -------------------------------------------------------------------- */
285 void GLBatch::bind(int i_first)
286 {
288 
289  if (flag & GPU_BATCH_DIRTY) {
290  flag &= ~GPU_BATCH_DIRTY;
291  vao_cache_.clear();
292  }
293 
294 #if GPU_TRACK_INDEX_RANGE
295  /* Can be removed if GL 4.3 is required. */
296  if (!GLContext::fixed_restart_index_support && (elem != nullptr)) {
297  glPrimitiveRestartIndex(this->elem_()->restart_index());
298  }
299 #endif
300 
301  /* Can be removed if GL 4.2 is required. */
302  if (!GLContext::base_instance_support && (i_first > 0)) {
303  glBindVertexArray(vao_cache_.base_instance_vao_get(this, i_first));
304  }
305  else {
306  glBindVertexArray(vao_cache_.vao_get(this));
307  }
308 }
309 
310 void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
311 {
312  GL_CHECK_RESOURCES("Batch");
313 
314  this->bind(i_first);
315 
316  BLI_assert(v_count > 0 && i_count > 0);
317 
318  GLenum gl_type = to_gl(prim_type);
319 
320  if (elem) {
321  const GLIndexBuf *el = this->elem_();
322  GLenum index_type = to_gl(el->index_type_);
323  GLint base_index = el->index_base_;
324  void *v_first_ofs = el->offset_ptr(v_first);
325 
327  glDrawElementsInstancedBaseVertexBaseInstance(
328  gl_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first);
329  }
330  else {
331  glDrawElementsInstancedBaseVertex(
332  gl_type, v_count, index_type, v_first_ofs, i_count, base_index);
333  }
334  }
335  else {
336 #ifdef __APPLE__
337  glDisable(GL_PRIMITIVE_RESTART);
338 #endif
340  glDrawArraysInstancedBaseInstance(gl_type, v_first, v_count, i_count, i_first);
341  }
342  else {
343  glDrawArraysInstanced(gl_type, v_first, v_count, i_count);
344  }
345 #ifdef __APPLE__
346  glEnable(GL_PRIMITIVE_RESTART);
347 #endif
348  }
349 }
350 
#define BLI_assert(a)
Definition: BLI_assert.h:58
GPUBatch
Definition: GPU_batch.h:93
@ GPU_BATCH_DIRTY
Definition: GPU_batch.h:61
#define GPU_BATCH_VAO_DYN_ALLOC_COUNT
Definition: GPU_batch.h:38
#define glEnable
#define glDisable
#define MEM_recallocN(vmemh, len)
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
GLVaoCache vao_cache_
Definition: gl_batch.hh:96
static bool base_instance_support
Definition: gl_context.hh:64
static bool fixed_restart_index_support
Definition: gl_context.hh:69
void vao_cache_unregister(GLVaoCache *cache)
Definition: gl_context.cc:298
static GLContext * get()
Definition: gl_context.hh:118
void vao_free(GLuint vao_id)
Definition: gl_context.cc:237
void vao_cache_register(GLVaoCache *cache)
Definition: gl_context.cc:291
void * offset_ptr(uint additional_vertex_offset) const
GLuint base_instance_vao_get(GPUBatch *batch, int i_first)
Definition: gl_batch.cc:227
struct blender::gpu::GLVaoCache::@645::@647 static_vaos
struct blender::gpu::GLVaoCache::@645::@648 dynamic_vaos
GLuint lookup(const GLShaderInterface *interface)
Definition: gl_batch.cc:195
const GLShaderInterface * interfaces[GPU_VAO_STATIC_LEN]
Definition: gl_batch.hh:65
void insert(const GLShaderInterface *interface, GLuint vao_id)
Definition: gl_batch.cc:78
GLuint vao_get(GPUBatch *batch)
Definition: gl_batch.cc:259
void remove(const GLShaderInterface *interface)
Definition: gl_batch.cc:136
virtual void apply_state(void)=0
GPUBatch * batch
Definition: drawnode.c:3779
#define GPU_VAO_STATIC_LEN
Definition: gl_batch.hh:44
#define GL_CHECK_RESOURCES(info)
Definition: gl_debug.hh:81
void KERNEL_FUNCTION_FULL_NAME() shader(KernelGlobals *kg, uint4 *input, float4 *output, int type, int filter, int i, int offset, int sample)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void update_bindings(const GLuint vao, const GPUBatch *batch, const ShaderInterface *interface, const int base_instance)
static GLenum to_gl(const GPUAttachmentType type)