Blender  V3.3
gl_drawlist.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 
11 #include "BLI_assert.h"
12 
13 #include "GPU_batch.h"
14 
15 #include "gpu_context_private.hh"
16 #include "gpu_drawlist_private.hh"
18 
19 #include "gl_backend.hh"
20 #include "gl_drawlist.hh"
21 #include "gl_primitive.hh"
22 
23 #include <climits>
24 
25 using namespace blender::gpu;
26 
27 struct GLDrawCommand {
28  GLuint v_count;
29  GLuint i_count;
30  GLuint v_first;
31  GLuint i_first;
32 };
33 
35  GLuint v_count;
36  GLuint i_count;
37  GLuint v_first;
38  GLuint base_index;
39  GLuint i_first;
40 };
41 
42 #define MDI_ENABLED (buffer_size_ != 0)
43 #define MDI_DISABLED (buffer_size_ == 0)
44 #define MDI_INDEXED (base_index_ != UINT_MAX)
45 
47 {
48  BLI_assert(length > 0);
49  batch_ = nullptr;
50  buffer_id_ = 0;
51  command_len_ = 0;
52  base_index_ = 0;
53  command_offset_ = 0;
54  data_size_ = 0;
55  data_ = nullptr;
56 
58  /* Alloc the biggest possible command list, which is indexed. */
59  buffer_size_ = sizeof(GLDrawCommandIndexed) * length;
60  }
61  else {
62  /* Indicates MDI is not supported. */
63  buffer_size_ = 0;
64  }
65  /* Force buffer specification on first init. */
66  data_offset_ = buffer_size_;
67 }
68 
70 {
71  GLContext::buf_free(buffer_id_);
72 }
73 
74 void GLDrawList::init()
75 {
78  BLI_assert(data_ == nullptr);
79  batch_ = nullptr;
80  command_len_ = 0;
81 
82  if (buffer_id_ == 0) {
83  /* Allocate on first use. */
84  glGenBuffers(1, &buffer_id_);
85  context_ = GLContext::get();
86  }
87 
88  glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_);
89  /* If buffer is full, orphan buffer data and start fresh. */
90  size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand);
91  if (data_offset_ + command_size > buffer_size_) {
92  glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, nullptr, GL_DYNAMIC_DRAW);
93  data_offset_ = 0;
94  }
95  /* Map the remaining range. */
96  GLbitfield flag = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
97  data_size_ = buffer_size_ - data_offset_;
98  data_ = (GLbyte *)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, data_offset_, data_size_, flag);
99  command_offset_ = 0;
100 }
101 
102 void GLDrawList::append(GPUBatch *gpu_batch, int i_first, int i_count)
103 {
104  /* Fallback when MultiDrawIndirect is not supported/enabled. */
105  if (MDI_DISABLED) {
106  GPU_batch_draw_advanced(gpu_batch, 0, 0, i_first, i_count);
107  return;
108  }
109 
110  if (data_ == nullptr) {
111  this->init();
112  }
113 
114  GLBatch *batch = static_cast<GLBatch *>(gpu_batch);
115  if (batch != batch_) {
116  // BLI_assert(batch->flag | GPU_BATCH_INIT);
117  this->submit();
118  batch_ = batch;
119  /* Cached for faster access. */
120  GLIndexBuf *el = batch_->elem_();
121  base_index_ = el ? el->index_base_ : UINT_MAX;
122  v_first_ = el ? el->index_start_ : 0;
123  v_count_ = el ? el->index_len_ : batch->verts_(0)->vertex_len;
124  }
125 
126  if (v_count_ == 0) {
127  /* Nothing to draw. */
128  return;
129  }
130 
131  if (MDI_INDEXED) {
132  GLDrawCommandIndexed *cmd = reinterpret_cast<GLDrawCommandIndexed *>(data_ + command_offset_);
133  cmd->v_first = v_first_;
134  cmd->v_count = v_count_;
135  cmd->i_count = i_count;
136  cmd->base_index = base_index_;
137  cmd->i_first = i_first;
138  }
139  else {
140  GLDrawCommand *cmd = reinterpret_cast<GLDrawCommand *>(data_ + command_offset_);
141  cmd->v_first = v_first_;
142  cmd->v_count = v_count_;
143  cmd->i_count = i_count;
144  cmd->i_first = i_first;
145  }
146 
147  size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand);
148 
149  command_offset_ += command_size;
150  command_len_++;
151 
152  /* Check if we can fit at least one other command. */
153  if (command_offset_ + command_size > data_size_) {
154  this->submit();
155  }
156 }
157 
159 {
160  if (command_len_ == 0) {
161  return;
162  }
163  /* Something's wrong if we get here without MDI support. */
165  BLI_assert(data_);
166  BLI_assert(GLContext::get()->shader != nullptr);
167 
168  size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand);
169 
170  /* Only do multi-draw indirect if doing more than 2 drawcall. This avoids the overhead of
171  * buffer mapping if scene is not very instance friendly. BUT we also need to take into
172  * account the case where only a few instances are needed to finish filling a call buffer. */
173  const bool is_finishing_a_buffer = (command_offset_ + command_size > data_size_);
174  if (command_len_ > 2 || is_finishing_a_buffer) {
175  GLenum prim = to_gl(batch_->prim_type);
176  void *offset = (void *)data_offset_;
177 
178  glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_);
179  glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, command_offset_);
180  glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
181  data_ = nullptr; /* Unmapped */
182  data_offset_ += command_offset_;
183 
184  batch_->bind(0);
185 
186  if (MDI_INDEXED) {
187  GLenum gl_type = to_gl(batch_->elem_()->index_type_);
188  glMultiDrawElementsIndirect(prim, gl_type, offset, command_len_, 0);
189  }
190  else {
191  glMultiDrawArraysIndirect(prim, offset, command_len_, 0);
192  }
193  }
194  else {
195  /* Fallback do simple drawcalls, and don't unmap the buffer. */
196  if (MDI_INDEXED) {
198  for (int i = 0; i < command_len_; i++, cmd++) {
199  /* Index start was already added. Avoid counting it twice. */
200  cmd->v_first -= v_first_;
201  batch_->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
202  }
203  /* Reuse the same data. */
204  command_offset_ -= command_len_ * sizeof(GLDrawCommandIndexed);
205  }
206  else {
207  GLDrawCommand *cmd = (GLDrawCommand *)data_;
208  for (int i = 0; i < command_len_; i++, cmd++) {
209  batch_->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count);
210  }
211  /* Reuse the same data. */
212  command_offset_ -= command_len_ * sizeof(GLDrawCommand);
213  }
214  }
215  /* Do not submit this buffer again. */
216  command_len_ = 0;
217  /* Avoid keeping reference to the batch. */
218  batch_ = nullptr;
219 }
#define BLI_assert(a)
Definition: BLI_assert.h:46
GPUBatch
Definition: GPU_batch.h:78
void GPU_batch_draw_advanced(GPUBatch *, int v_first, int v_count, int i_first, int i_count)
Definition: gpu_batch.cc:243
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
static void buf_free(GLuint buf_id)
Definition: gl_context.cc:250
static GLContext * get()
Definition: gl_context.hh:117
static bool multi_draw_indirect_support
Definition: gl_context.hh:64
void append(GPUBatch *batch, int i_first, int i_count) override
Definition: gl_drawlist.cc:102
void submit() override
Definition: gl_drawlist.cc:158
#define MDI_INDEXED
Definition: gl_drawlist.cc:44
#define MDI_ENABLED
Definition: gl_drawlist.cc:42
#define MDI_DISABLED
Definition: gl_drawlist.cc:43
struct @653::@655 batch
#define UINT_MAX
Definition: hash_md5.c:43
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
static GLenum to_gl(const GPUAttachmentType type)
T length(const vec_base< T, Size > &a)
GLuint v_first
Definition: gl_drawlist.cc:30
GLuint i_first
Definition: gl_drawlist.cc:31
GLuint v_count
Definition: gl_drawlist.cc:28
GLuint i_count
Definition: gl_drawlist.cc:29