Blender  V3.3
gl_batch.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2016 by Mike Erwin. All rights reserved. */
3 
12 #include "BLI_assert.h"
13 
14 #include "gpu_batch_private.hh"
15 #include "gpu_shader_private.hh"
16 
17 #include "gl_context.hh"
18 #include "gl_debug.hh"
19 #include "gl_index_buffer.hh"
20 #include "gl_primitive.hh"
21 #include "gl_vertex_array.hh"
22 
23 #include "gl_batch.hh"
24 
25 using namespace blender::gpu;
26 
27 /* -------------------------------------------------------------------- */
35 {
36  init();
37 }
38 
40 {
41  this->clear();
42 }
43 
44 void GLVaoCache::init()
45 {
46  context_ = nullptr;
47  interface_ = nullptr;
48  is_dynamic_vao_count = false;
49  for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
50  static_vaos.interfaces[i] = nullptr;
51  static_vaos.vao_ids[i] = 0;
52  }
53  vao_base_instance_ = 0;
54  base_instance_ = 0;
55  vao_id_ = 0;
56 }
57 
58 void GLVaoCache::insert(const GLShaderInterface *interface, GLuint vao)
59 {
60  /* Now insert the cache. */
61  if (!is_dynamic_vao_count) {
62  int i; /* find first unused slot */
63  for (i = 0; i < GPU_VAO_STATIC_LEN; i++) {
64  if (static_vaos.vao_ids[i] == 0) {
65  break;
66  }
67  }
68 
69  if (i < GPU_VAO_STATIC_LEN) {
70  static_vaos.interfaces[i] = interface;
71  static_vaos.vao_ids[i] = vao;
72  }
73  else {
74  /* Erase previous entries, they will be added back if drawn again. */
75  for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
76  if (static_vaos.interfaces[i] != nullptr) {
77  const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this);
78  context_->vao_free(static_vaos.vao_ids[i]);
79  }
80  }
81  /* Not enough place switch to dynamic. */
82  is_dynamic_vao_count = true;
83  /* Init dynamic arrays and let the branch below set the values. */
85  dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_callocN(
86  dynamic_vaos.count * sizeof(GLShaderInterface *), "dyn vaos interfaces");
87  dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(dynamic_vaos.count * sizeof(GLuint),
88  "dyn vaos ids");
89  }
90  }
91 
92  if (is_dynamic_vao_count) {
93  int i; /* find first unused slot */
94  for (i = 0; i < dynamic_vaos.count; i++) {
95  if (dynamic_vaos.vao_ids[i] == 0) {
96  break;
97  }
98  }
99 
100  if (i == dynamic_vaos.count) {
101  /* Not enough place, realloc the array. */
102  i = dynamic_vaos.count;
104  dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_recallocN(
105  (void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * dynamic_vaos.count);
106  dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(dynamic_vaos.vao_ids,
107  sizeof(GLuint) * dynamic_vaos.count);
108  }
109  dynamic_vaos.interfaces[i] = interface;
110  dynamic_vaos.vao_ids[i] = vao;
111  }
112 
113  const_cast<GLShaderInterface *>(interface)->ref_add(this);
114 }
115 
116 void GLVaoCache::remove(const GLShaderInterface *interface)
117 {
118  const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
119  GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
120  const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
121  static_vaos.interfaces;
122  for (int i = 0; i < count; i++) {
123  if (interfaces[i] == interface) {
124  context_->vao_free(vaos[i]);
125  vaos[i] = 0;
126  interfaces[i] = nullptr;
127  break; /* cannot have duplicates */
128  }
129  }
130 }
131 
133 {
134  GLContext *ctx = GLContext::get();
135  const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
136  GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
137  const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
138  static_vaos.interfaces;
139  /* Early out, nothing to free. */
140  if (context_ == nullptr) {
141  return;
142  }
143 
144  if (context_ == ctx) {
145  glDeleteVertexArrays(count, vaos);
146  glDeleteVertexArrays(1, &vao_base_instance_);
147  }
148  else {
149  /* TODO(fclem): Slow way. Could avoid multiple mutex lock here */
150  for (int i = 0; i < count; i++) {
151  context_->vao_free(vaos[i]);
152  }
153  context_->vao_free(vao_base_instance_);
154  }
155 
156  for (int i = 0; i < count; i++) {
157  if (interfaces[i] != nullptr) {
158  const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this);
159  }
160  }
161 
162  if (is_dynamic_vao_count) {
163  MEM_freeN((void *)dynamic_vaos.interfaces);
164  MEM_freeN(dynamic_vaos.vao_ids);
165  }
166 
167  if (context_) {
168  context_->vao_cache_unregister(this);
169  }
170  /* Reinit. */
171  this->init();
172 }
173 
174 GLuint GLVaoCache::lookup(const GLShaderInterface *interface)
175 {
176  const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
177  const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
178  static_vaos.interfaces;
179  for (int i = 0; i < count; i++) {
180  if (interfaces[i] == interface) {
181  return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i];
182  }
183  }
184  return 0;
185 }
186 
187 void GLVaoCache::context_check()
188 {
189  GLContext *ctx = GLContext::get();
190  BLI_assert(ctx);
191 
192  if (context_ != ctx) {
193  if (context_ != nullptr) {
194  /* IMPORTANT: Trying to draw a batch in multiple different context will trash the VAO cache.
195  * This has major performance impact and should be avoided in most cases. */
196  context_->vao_cache_unregister(this);
197  }
198  this->clear();
199  context_ = ctx;
200  context_->vao_cache_register(this);
201  }
202 }
203 
205 {
206  this->context_check();
207  /* Make sure the interface is up to date. */
208  Shader *shader = GLContext::get()->shader;
209  GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
210  if (interface_ != interface) {
211  vao_get(batch);
212  /* Trigger update. */
213  base_instance_ = 0;
214  }
219 #ifdef __APPLE__
220  glDeleteVertexArrays(1, &vao_base_instance_);
221  vao_base_instance_ = 0;
222  base_instance_ = 0;
223 #endif
224 
225  if (vao_base_instance_ == 0) {
226  glGenVertexArrays(1, &vao_base_instance_);
227  }
228 
229  if (base_instance_ != i_first) {
230  base_instance_ = i_first;
231  GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first);
232  }
233  return vao_base_instance_;
234 }
235 
237 {
238  this->context_check();
239 
240  Shader *shader = GLContext::get()->shader;
241  GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
242  if (interface_ != interface) {
243  interface_ = interface;
244  vao_id_ = this->lookup(interface_);
245 
246  if (vao_id_ == 0) {
247  /* Cache miss, create a new VAO. */
248  glGenVertexArrays(1, &vao_id_);
249  this->insert(interface_, vao_id_);
250  GLVertArray::update_bindings(vao_id_, batch, interface_, 0);
251  }
252  }
253 
254  return vao_id_;
255 }
256 
259 /* -------------------------------------------------------------------- */
263 void GLBatch::bind(int i_first)
264 {
266 
267  if (flag & GPU_BATCH_DIRTY) {
268  flag &= ~GPU_BATCH_DIRTY;
269  vao_cache_.clear();
270  }
271 
272 #if GPU_TRACK_INDEX_RANGE
273  /* Can be removed if GL 4.3 is required. */
274  if (!GLContext::fixed_restart_index_support && (elem != nullptr)) {
275  glPrimitiveRestartIndex(this->elem_()->restart_index());
276  }
277 #endif
278 
279  /* Can be removed if GL 4.2 is required. */
280  if (!GLContext::base_instance_support && (i_first > 0)) {
281  glBindVertexArray(vao_cache_.base_instance_vao_get(this, i_first));
282  }
283  else {
284  glBindVertexArray(vao_cache_.vao_get(this));
285  }
286 }
287 
288 void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
289 {
290  GL_CHECK_RESOURCES("Batch");
291 
292  this->bind(i_first);
293 
294  BLI_assert(v_count > 0 && i_count > 0);
295 
296  GLenum gl_type = to_gl(prim_type);
297 
298  if (elem) {
299  const GLIndexBuf *el = this->elem_();
300  GLenum index_type = to_gl(el->index_type_);
301  GLint base_index = el->index_base_;
302  void *v_first_ofs = el->offset_ptr(v_first);
303 
305  glDrawElementsInstancedBaseVertexBaseInstance(
306  gl_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first);
307  }
308  else {
309  glDrawElementsInstancedBaseVertex(
310  gl_type, v_count, index_type, v_first_ofs, i_count, base_index);
311  }
312  }
313  else {
314 #ifdef __APPLE__
315  glDisable(GL_PRIMITIVE_RESTART);
316 #endif
318  glDrawArraysInstancedBaseInstance(gl_type, v_first, v_count, i_count, i_first);
319  }
320  else {
321  glDrawArraysInstanced(gl_type, v_first, v_count, i_count);
322  }
323 #ifdef __APPLE__
324  glEnable(GL_PRIMITIVE_RESTART);
325 #endif
326  }
327 }
328 
#define BLI_assert(a)
Definition: BLI_assert.h:46
GPUBatch
Definition: GPU_batch.h:78
@ GPU_BATCH_DIRTY
Definition: GPU_batch.h:46
#define GPU_BATCH_VAO_DYN_ALLOC_COUNT
Definition: GPU_batch.h:23
#define glEnable
#define glDisable
#define MEM_recallocN(vmemh, len)
GLIndexBuf * elem_() const
Definition: gl_batch.hh:100
void bind(int i_first)
Definition: gl_batch.cc:263
void draw(int v_first, int v_count, int i_first, int i_count) override
Definition: gl_batch.cc:288
GLVaoCache vao_cache_
Definition: gl_batch.hh:92
static bool base_instance_support
Definition: gl_context.hh:53
static bool fixed_restart_index_support
Definition: gl_context.hh:60
void vao_cache_unregister(GLVaoCache *cache)
Definition: gl_context.cc:291
static GLContext * get()
Definition: gl_context.hh:117
void vao_free(GLuint vao_id)
Definition: gl_context.cc:230
void vao_cache_register(GLVaoCache *cache)
Definition: gl_context.cc:284
void * offset_ptr(uint additional_vertex_offset) const
GLuint base_instance_vao_get(GPUBatch *batch, int i_first)
Definition: gl_batch.cc:204
struct blender::gpu::GLVaoCache::@687::@689 static_vaos
GLuint lookup(const GLShaderInterface *interface)
Definition: gl_batch.cc:174
const GLShaderInterface * interfaces[GPU_VAO_STATIC_LEN]
Definition: gl_batch.hh:51
void insert(const GLShaderInterface *interface, GLuint vao_id)
Definition: gl_batch.cc:58
GLuint vao_get(GPUBatch *batch)
Definition: gl_batch.cc:236
struct blender::gpu::GLVaoCache::@687::@690 dynamic_vaos
void remove(const GLShaderInterface *interface)
Definition: gl_batch.cc:116
ShaderInterface * interface
virtual void apply_state()=0
#define GPU_VAO_STATIC_LEN
Definition: gl_batch.hh:28
#define GL_CHECK_RESOURCES(info)
Definition: gl_debug.hh:67
struct @653::@655 batch
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void update_bindings(const GLuint vao, const GPUBatch *batch, const ShaderInterface *interface, int base_instance)
static GLenum to_gl(const GPUAttachmentType type)