Blender V5.0
gpu_batch.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include "BLI_math_base.h"
13#include "BLI_utildefines.h"
14
15#include "GPU_batch.hh"
16#include "GPU_batch_presets.hh"
17#include "GPU_shader.hh"
18
19#include "GPU_index_buffer.hh"
20#include "GPU_vertex_buffer.hh"
21#include "gpu_backend.hh"
23#include "gpu_debug_private.hh"
24#include "gpu_shader_private.hh"
25
26#include <cstring>
27
28using namespace blender::gpu;
29
30/* -------------------------------------------------------------------- */
33
35{
36 std::fill_n(batch->verts, ARRAY_SIZE(batch->verts), nullptr);
37 batch->elem = nullptr;
38 batch->flag = GPUBatchFlag(0);
39 batch->prim_type = GPUPrimType(0);
40 batch->shader = nullptr;
41 batch->procedural_vertices = -1;
42}
43
45{
46 Batch *batch = GPUBackend::get()->batch_alloc();
48 return batch;
49}
50
51Batch *GPU_batch_create_ex(GPUPrimType primitive_type,
52 VertBuf *vertex_buf,
53 IndexBuf *index_buf,
54 GPUBatchFlag owns_flag)
55{
56 Batch *batch = GPU_batch_calloc();
57 GPU_batch_init_ex(batch, primitive_type, vertex_buf, index_buf, owns_flag);
58 return batch;
59}
60
62 GPUPrimType primitive_type,
63 VertBuf *vertex_buf,
64 IndexBuf *index_buf,
65 GPUBatchFlag owns_flag)
66{
67 /* Do not pass any other flag */
68 BLI_assert((owns_flag & ~(GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX)) == 0);
69 /* Batch needs to be in cleared state. */
70 BLI_assert((batch->flag & GPU_BATCH_INIT) == 0);
71
72 batch->verts[0] = vertex_buf;
73 for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) {
74 batch->verts[v] = nullptr;
75 }
76 batch->elem = index_buf;
77 batch->prim_type = primitive_type;
78 batch->flag = owns_flag | GPU_BATCH_INIT | GPU_BATCH_DIRTY;
79 batch->shader = nullptr;
80 batch->procedural_vertices = -1;
81}
82
83Batch *GPU_batch_create_procedural(GPUPrimType primitive_type, int32_t vertex_count)
84{
85 BLI_assert(vertex_count >= 0);
86 Batch *batch = GPU_batch_calloc();
87 for (auto &v : batch->verts) {
88 v = nullptr;
89 }
90 batch->elem = nullptr;
91 batch->prim_type = primitive_type;
93 batch->shader = nullptr;
94 batch->procedural_vertices = vertex_count;
95 return batch;
96}
97
98void GPU_batch_copy(Batch *batch_dst, Batch *batch_src)
99{
100 GPU_batch_clear(batch_dst);
102 batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, GPU_BATCH_INVALID);
103
104 batch_dst->prim_type = batch_src->prim_type;
105 for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) {
106 batch_dst->verts[v] = batch_src->verts[v];
107 }
108 batch_dst->procedural_vertices = batch_src->procedural_vertices;
109}
110
112{
113 if (batch->flag & GPU_BATCH_OWNS_INDEX) {
115 }
116 if (batch->flag & GPU_BATCH_OWNS_VBO_ANY) {
117 for (int v = 0; (v < GPU_BATCH_VBO_MAX_LEN) && batch->verts[v]; v++) {
118 if (batch->flag & (GPU_BATCH_OWNS_VBO << v)) {
120 }
121 }
122 }
123 batch->flag = GPU_BATCH_INVALID;
124 batch->procedural_vertices = -1;
125}
126
128{
130 delete batch;
131}
132
134
135/* -------------------------------------------------------------------- */
138
139void GPU_batch_elembuf_set(Batch *batch, blender::gpu::IndexBuf *index_buf, bool own_ibo)
140{
141 BLI_assert(index_buf);
142 batch->flag |= GPU_BATCH_DIRTY;
143
144 if (batch->elem && (batch->flag & GPU_BATCH_OWNS_INDEX)) {
146 }
147 batch->elem = index_buf;
148
150}
151
152int GPU_batch_vertbuf_add(Batch *batch, VertBuf *vertex_buf, bool own_vbo)
153{
154 BLI_assert(vertex_buf);
155 batch->flag |= GPU_BATCH_DIRTY;
156
157 for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
158 if (batch->verts[v] == nullptr) {
159 /* for now all VertexBuffers must have same vertex_len */
160 if (batch->verts[0] != nullptr) {
161 /* This is an issue for the HACK inside DRW_vbo_request(). */
162 // BLI_assert(verts->vertex_len == batch->verts[0]->vertex_len);
163 }
164 batch->verts[v] = vertex_buf;
166 return v;
167 }
168 }
169 /* we only make it this far if there is no room for another VertBuf */
170 BLI_assert_msg(0, "Not enough VBO slot in batch");
171 return -1;
172}
173
174bool GPU_batch_vertbuf_has(const Batch *batch, const VertBuf *vertex_buf)
175{
176 for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
177 if (batch->verts[v] == vertex_buf) {
178 return true;
179 }
180 }
181 return false;
182}
183
185
186/* -------------------------------------------------------------------- */
190
193 const shader::SpecializationConstants *constants_state)
194{
195 batch->shader = shader;
196 GPU_shader_bind(batch->shader, constants_state);
197}
198
201 VertBuf *vbo)
202{
203 const GPUVertFormat *format = &vbo->format;
204
205 /* We need to support GPU OpenSubdiv meshes. This assert can be enabled back after we refactor
206 * our OpenSubdiv implementation to output the same layout as the regular mesh extraction. */
207 // BLI_assert_msg(format->attr_len == 1, "Multi attribute buffers are not supported for now");
208
209 char uniform_name[] = "gpu_attr_0";
210 uint stride = format->stride;
211 uint offset = 0;
212 uint16_t bound_attr = 0u;
213 for (uint a_idx = 0; a_idx < format->attr_len; a_idx++) {
214 const GPUVertAttr *a = &format->attrs[a_idx];
215
216 if (format->deinterleaved) {
217 offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].type.size()) * vbo->vertex_len;
218 stride = a->type.size();
219 }
220 else {
221 offset = a->offset;
222 }
223
224 for (uint n_idx = 0; n_idx < a->name_len; n_idx++) {
225 const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
226 const ShaderInput *input = interface->ssbo_get(name);
227 if (input == nullptr || input->location == -1) {
228 continue;
229 }
230 GPU_vertbuf_bind_as_ssbo(vbo, input->location);
231 bound_attr |= (1 << input->location);
232
233 /* WORKAROUND: This is to support complex format. But ideally this should not be supported.
234 */
235 uniform_name[9] = '0' + input->location;
236
237 /* Only support 4byte aligned attributes. */
238 BLI_assert((stride % 4) == 0);
239 BLI_assert((offset % 4) == 0);
240 int descriptor[2] = {int(stride) / 4, int(offset) / 4};
242 /* WORKAROUND: Fix for polyline workaround. Ideally should be fused with `gpu_attr_0`.
243 * But for now, changes are a bit too invasive. Will need to be revisited later on. */
244 char uniform_name_len[] = "gpu_attr_0_len";
245 uniform_name_len[9] = '0' + input->location;
246 GPU_shader_uniform_1i(shader, uniform_name_len, a->type.comp_len());
247 }
248 }
249 return bound_attr;
250}
251
254 const shader::SpecializationConstants *constants)
255{
256 const ShaderInterface *interface = shader->interface;
257 if (interface->ssbo_attr_mask_ == 0) {
258 return;
259 }
260
261 uint16_t ssbo_attributes = interface->ssbo_attr_mask_;
262
263 if (ssbo_attributes & (1 << GPU_SSBO_INDEX_BUF_SLOT)) {
264 /* Ensure binding for setting uniforms. This is required by the OpenGL backend. */
265 GPU_shader_bind(shader, constants);
266 if (batch->elem) {
268 GPU_shader_uniform_1b(shader, "gpu_index_no_buffer", false);
269 GPU_shader_uniform_1b(shader, "gpu_index_16bit", !batch->elem->is_32bit());
270 GPU_shader_uniform_1i(shader, "gpu_index_base_index", batch->elem->index_base_get());
271 }
272 else {
273 /* Still fulfill the binding requirements even if the buffer will not be read. */
275 GPU_shader_uniform_1b(shader, "gpu_index_no_buffer", true);
276 }
277 ssbo_attributes &= ~(1 << GPU_SSBO_INDEX_BUF_SLOT);
278 }
279
280 /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */
281 for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) {
282 VertBuf *vbo = batch->verts_(v);
283 if (vbo) {
284 ssbo_attributes &= ~bind_attribute_as_ssbo(interface, shader, vbo);
285 }
286 }
287
288 BLI_assert_msg(ssbo_attributes == 0, "Not all attribute storage buffer fulfilled");
289 UNUSED_VARS_NDEBUG(ssbo_attributes);
290}
291
293
294/* -------------------------------------------------------------------- */
297
299 int *r_vertex_count,
300 int *r_vertex_first,
301 int *r_base_index,
302 int *r_instance_count)
303{
304 if (batch->procedural_vertices >= 0) {
305 *r_vertex_count = batch->procedural_vertices;
306 *r_vertex_first = 0;
307 *r_base_index = -1;
308 }
309 else if (batch->elem) {
310 *r_vertex_count = batch->elem_()->index_len_get();
311 *r_vertex_first = batch->elem_()->index_start_get();
312 *r_base_index = batch->elem_()->index_base_get();
313 }
314 else {
315 *r_vertex_count = batch->verts_(0)->vertex_len;
316 *r_vertex_first = 0;
317 *r_base_index = -1;
318 }
319
320 *r_instance_count = 1;
321}
322
324 GPUPrimType output_prim_type,
325 int vertex_count,
326 int vertex_first,
327 int output_primitive_cout)
328{
329 int vert_per_original_primitive = indices_per_primitive(input_prim_type);
330 int vert_per_expanded_primitive = indices_per_primitive(output_prim_type);
331
332 int prim_first = vertex_first / vert_per_original_primitive;
333 int prim_len = vertex_count / vert_per_original_primitive;
334
335 BLI_assert_msg(vert_per_original_primitive != -1,
336 "Primitive expansion only works for primitives with known amount of vertices");
337
338 /* WORKAROUND: Needed for polyline_draw_workaround. */
339 if (input_prim_type == GPU_PRIM_LINE_STRIP) {
340 prim_len = vertex_count - 1;
341 }
342
343 int out_vertex_first = prim_first * vert_per_expanded_primitive * output_primitive_cout;
344 int out_vertex_count = prim_len * vert_per_expanded_primitive * output_primitive_cout;
345
346 return blender::IndexRange(out_vertex_first, out_vertex_count);
347}
348
350 Batch *batch, int vertex_first, int vertex_count, int instance_first, int instance_count)
351{
352 /* Early out as this can cause crashes on some backend (see #136831). */
353 if (vertex_count == 0) {
354 return;
355 }
356 /* Check compatible input primitive. */
358
361 batch->prim_type, GPU_PRIM_TRIS, vertex_count, vertex_first, 2);
362 Batch *tri_batch = Context::get()->procedural_triangles_batch_get();
363 GPU_batch_set_shader(tri_batch, batch->shader);
364
365 int vert_stride_count[3] = {(batch->prim_type == GPU_PRIM_LINES) ? 2 : 1, vertex_count, 0};
366 GPU_shader_uniform_3iv(batch->shader, "gpu_vert_stride_count_offset", vert_stride_count);
367 /* Assume GPU_FETCH_FLOAT for now. A bit cumbersome to assert for this or to find the correct
368 * attribute. */
369 GPU_shader_uniform_1b(batch->shader, "gpu_attr_0_fetch_int", false);
370
371 /* Allow byte color fetch. */
373 int id = GPU_vertformat_attr_id_get(format, "color");
374 if (id != -1) {
375 const GPUVertAttr &attr = format->attrs[id];
376 const bool is_unorm8 = attr.type.format == blender::gpu::VertAttrType::UNORM_8_8_8_8;
377 BLI_assert_msg(is_unorm8 || attr.type.fetch_mode() == GPU_FETCH_FLOAT,
378 "color attribute for polylines can only use GPU_FETCH_INT_TO_FLOAT_UNIT or "
379 "GPU_FETCH_FLOAT");
380 GPU_shader_uniform_1b(batch->shader, "gpu_attr_1_fetch_unorm8", is_unorm8);
381 }
382
383 GPU_batch_draw_advanced(tri_batch, range.start(), range.size(), instance_first, instance_count);
384}
385
387{
388 BLI_assert(batch != nullptr);
389 GPU_shader_bind(batch->shader);
390 if (batch->shader->is_polyline) {
391 polyline_draw_workaround(batch, 0, batch->vertex_count_get(), 0, 0);
392 }
393 else {
394 GPU_batch_draw_advanced(batch, 0, 0, 0, 0);
395 }
396}
397
398void GPU_batch_draw_range(Batch *batch, int vertex_first, int vertex_count)
399{
400 BLI_assert(batch != nullptr);
401 GPU_shader_bind(batch->shader);
402 if (batch->shader->is_polyline) {
403 polyline_draw_workaround(batch, vertex_first, vertex_count, 0, 0);
404 }
405 else {
406 GPU_batch_draw_advanced(batch, vertex_first, vertex_count, 0, 0);
407 }
408}
409
410void GPU_batch_draw_instance_range(Batch *batch, int instance_first, int instance_count)
411{
412 BLI_assert(batch != nullptr);
413 /* Not polyline shaders support instancing. */
414 BLI_assert(batch->shader->is_polyline == false);
415
416 GPU_shader_bind(batch->shader);
417 GPU_batch_draw_advanced(batch, 0, 0, instance_first, instance_count);
418}
419
421 Batch *batch, int vertex_first, int vertex_count, int instance_first, int instance_count)
422{
423 BLI_assert(batch != nullptr);
424 BLI_assert(Context::get()->shader != nullptr);
426
427 if (vertex_count == 0) {
428 if (batch->procedural_vertices > 0) {
429 vertex_count = batch->procedural_vertices;
430 }
431 else if (batch->elem) {
432 vertex_count = batch->elem_()->index_len_get();
433 }
434 else {
435 vertex_count = batch->verts_(0)->vertex_len;
436 }
437 }
438 if (instance_count == 0) {
439 instance_count = 1;
440 }
441
442 if (vertex_count == 0 || instance_count == 0) {
443 /* Nothing to draw. */
444 return;
445 }
446
447#ifndef NDEBUG
449#endif
450
451 batch->draw(vertex_first, vertex_count, instance_first, instance_count);
452}
453
454void GPU_batch_draw_indirect(Batch *batch, blender::gpu::StorageBuf *indirect_buf, intptr_t offset)
455{
456 BLI_assert(batch != nullptr);
457 BLI_assert(indirect_buf != nullptr);
458 BLI_assert(Context::get()->shader != nullptr);
460
461#ifndef NDEBUG
463#endif
464
465 batch->draw_indirect(indirect_buf, offset);
466}
467
469 blender::gpu::StorageBuf *indirect_buf,
470 int count,
471 intptr_t offset,
472 intptr_t stride)
473{
474 BLI_assert(batch != nullptr);
475 BLI_assert(indirect_buf != nullptr);
476 BLI_assert(Context::get()->shader != nullptr);
478
479#ifndef NDEBUG
481#endif
482
483 batch->multi_draw_indirect(indirect_buf, count, offset, stride);
484}
485
487
488/* -------------------------------------------------------------------- */
491
499
504
509
514
519
524
529
531
532/* -------------------------------------------------------------------- */
535
540
545
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
unsigned int uint
#define ARRAY_SIZE(arr)
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
static constexpr int GPU_BATCH_VBO_MAX_LEN
Definition GPU_batch.hh:33
GPUBatchFlag
Definition GPU_batch.hh:37
@ GPU_BATCH_INVALID
Definition GPU_batch.hh:39
@ GPU_BATCH_DIRTY
Definition GPU_batch.hh:53
@ GPU_BATCH_INIT
Definition GPU_batch.hh:49
@ GPU_BATCH_OWNS_INDEX
Definition GPU_batch.hh:46
@ GPU_BATCH_OWNS_VBO
Definition GPU_batch.hh:42
@ GPU_BATCH_OWNS_VBO_ANY
Definition GPU_batch.hh:44
void gpu_batch_presets_init()
void gpu_batch_presets_exit()
void GPU_indexbuf_discard(blender::gpu::IndexBuf *elem)
void GPU_indexbuf_bind_as_ssbo(blender::gpu::IndexBuf *elem, int binding)
GPUPrimType
@ GPU_PRIM_LINE_LOOP
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
@ GPU_PRIM_TRIS
void GPU_shader_uniform_1b(blender::gpu::Shader *sh, const char *name, bool value)
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_uniform_1i(blender::gpu::Shader *sh, const char *name, int value)
void GPU_shader_uniform_2iv(blender::gpu::Shader *sh, const char *name, const int data[2])
void GPU_shader_uniform_3iv(blender::gpu::Shader *sh, const char *name, const int data[3])
GPUBuiltinShader
@ GPU_SHADER_CFG_DEFAULT
blender::gpu::Shader * GPU_shader_get_builtin_shader_with_config(GPUBuiltinShader shader, GPUShaderConfig sh_cfg)
#define GPU_VERTBUF_DISCARD_SAFE(verts)
const GPUVertFormat * GPU_vertbuf_get_format(const blender::gpu::VertBuf *verts)
void GPU_vertbuf_bind_as_ssbo(blender::gpu::VertBuf *verts, int binding)
BLI_INLINE const char * GPU_vertformat_attr_name_get(const GPUVertFormat *format, const GPUVertAttr *attr, uint n_idx)
int GPU_vertformat_attr_id_get(const GPUVertFormat *, blender::StringRef name)
@ GPU_FETCH_FLOAT
ATTR_WARN_UNUSED_RESULT const BMVert * v
constexpr int64_t size() const
constexpr int64_t start() const
static Context * get()
Batch * procedural_triangles_batch_get()
void assert_framebuffer_shader_compatibility(Shader *sh)
Batch * procedural_triangle_strips_batch_get()
Batch * procedural_lines_batch_get()
Batch * procedural_points_batch_get()
static GPUBackend * get()
virtual Batch * batch_alloc()=0
static uint16_t bind_attribute_as_ssbo(const ShaderInterface *interface, blender::gpu::Shader *shader, VertBuf *vbo)
Definition gpu_batch.cc:199
blender::gpu::Batch * GPU_batch_procedural_triangle_strips_get()
Definition gpu_batch.cc:525
void GPU_batch_draw_parameter_get(Batch *batch, int *r_vertex_count, int *r_vertex_first, int *r_base_index, int *r_instance_count)
Definition gpu_batch.cc:298
void GPU_batch_discard(Batch *batch)
Definition gpu_batch.cc:127
void GPU_batch_program_set_builtin_with_config(Batch *batch, GPUBuiltinShader shader_id, GPUShaderConfig sh_cfg)
Definition gpu_batch.cc:492
bool GPU_batch_vertbuf_has(const Batch *batch, const VertBuf *vertex_buf)
Definition gpu_batch.cc:174
static void polyline_draw_workaround(Batch *batch, int vertex_first, int vertex_count, int instance_first, int instance_count)
Definition gpu_batch.cc:349
blender::gpu::Batch * GPU_batch_procedural_lines_get()
Definition gpu_batch.cc:515
void GPU_batch_set_shader(Batch *batch, blender::gpu::Shader *shader, const shader::SpecializationConstants *constants_state)
Definition gpu_batch.cc:191
void GPU_batch_program_set_imm_shader(Batch *batch)
Definition gpu_batch.cc:505
Batch * GPU_batch_create_procedural(GPUPrimType primitive_type, int32_t vertex_count)
Definition gpu_batch.cc:83
void GPU_batch_draw(Batch *batch)
Definition gpu_batch.cc:386
void GPU_batch_draw_instance_range(Batch *batch, int instance_first, int instance_count)
Definition gpu_batch.cc:410
void GPU_batch_draw_indirect(Batch *batch, blender::gpu::StorageBuf *indirect_buf, intptr_t offset)
Definition gpu_batch.cc:454
blender::IndexRange GPU_batch_draw_expanded_parameter_get(GPUPrimType input_prim_type, GPUPrimType output_prim_type, int vertex_count, int vertex_first, int output_primitive_cout)
Definition gpu_batch.cc:323
Batch * GPU_batch_calloc()
Definition gpu_batch.cc:44
void GPU_batch_draw_range(Batch *batch, int vertex_first, int vertex_count)
Definition gpu_batch.cc:398
Batch * GPU_batch_create_ex(GPUPrimType primitive_type, VertBuf *vertex_buf, IndexBuf *index_buf, GPUBatchFlag owns_flag)
Definition gpu_batch.cc:51
int GPU_batch_vertbuf_add(Batch *batch, VertBuf *vertex_buf, bool own_vbo)
Definition gpu_batch.cc:152
blender::gpu::Batch * GPU_batch_procedural_points_get()
Definition gpu_batch.cc:510
void GPU_batch_init_ex(Batch *batch, GPUPrimType primitive_type, VertBuf *vertex_buf, IndexBuf *index_buf, GPUBatchFlag owns_flag)
Definition gpu_batch.cc:61
void GPU_batch_multi_draw_indirect(Batch *batch, blender::gpu::StorageBuf *indirect_buf, int count, intptr_t offset, intptr_t stride)
Definition gpu_batch.cc:468
void GPU_batch_program_set_builtin(Batch *batch, GPUBuiltinShader shader_id)
Definition gpu_batch.cc:500
blender::gpu::Batch * GPU_batch_procedural_triangles_get()
Definition gpu_batch.cc:520
void GPU_batch_zero(Batch *batch)
Definition gpu_batch.cc:34
void gpu_batch_init()
Definition gpu_batch.cc:536
void GPU_batch_copy(Batch *batch_dst, Batch *batch_src)
Definition gpu_batch.cc:98
void GPU_batch_clear(Batch *batch)
Definition gpu_batch.cc:111
void GPU_batch_elembuf_set(Batch *batch, blender::gpu::IndexBuf *index_buf, bool own_ibo)
Definition gpu_batch.cc:139
void GPU_batch_bind_as_resources(Batch *batch, blender::gpu::Shader *shader, const shader::SpecializationConstants *constants)
Definition gpu_batch.cc:252
void GPU_batch_draw_advanced(Batch *batch, int vertex_first, int vertex_count, int instance_first, int instance_count)
Definition gpu_batch.cc:420
void gpu_batch_exit()
Definition gpu_batch.cc:541
struct @021025263243242147216143265077100330027142264337::@225245033123204053237120173316075113304004012000 batch
blender::gpu::Shader * immGetShader()
#define input
#define interface
#define GPU_SSBO_INDEX_BUF_SLOT
int count
format
descriptor
int indices_per_primitive(GPUPrimType prim_type)
void debug_validate_binding_image_format()
Definition gpu_debug.cc:170
const char * name
blender::gpu::VertAttrType format
GPUVertFetchMode fetch_mode() const
struct GPUVertAttr::Type type