Blender V5.0
gpu_uniform_buffer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10#include <cstring>
11
12#include "BLI_listbase.h"
13#include "BLI_math_base.h"
14#include "BLI_string.h"
15
16#include "BKE_global.hh"
17
18#include "gpu_backend.hh"
19#include "gpu_node_graph.hh"
20
21#include "GPU_capabilities.hh"
22#include "GPU_context.hh"
23#include "GPU_material.hh"
24
25#include "GPU_uniform_buffer.hh"
28
29/* -------------------------------------------------------------------- */
32
33namespace blender::gpu {
34
35UniformBuf::UniformBuf(size_t size, const char *name)
36{
37 /* Make sure that UBO is padded to size of vec4 */
38 BLI_assert((size % 16) == 0);
39
41
43}
44
49
50} // namespace blender::gpu
51
53
54/* -------------------------------------------------------------------- */
57
63{
64 GPUInput *input = (GPUInput *)link->data;
65 GPUType gputype = input->type;
66 /* Metal cannot pack floats after vec3. */
68 return (gputype == GPU_VEC3) ? GPU_VEC4 : gputype;
69 }
70 /* Unless the vec3 is followed by a float we need to treat it as a vec4. */
71 if (gputype == GPU_VEC3 && (link->next != nullptr) &&
72 (((GPUInput *)link->next->data)->type != GPU_FLOAT))
73 {
74 gputype = GPU_VEC4;
75 }
76 return gputype;
77}
78
83static int inputs_cmp(const void *a, const void *b)
84{
85 const LinkData *link_a = (const LinkData *)a, *link_b = (const LinkData *)b;
86 const GPUInput *input_a = (const GPUInput *)link_a->data;
87 const GPUInput *input_b = (const GPUInput *)link_b->data;
88 return input_a->type < input_b->type ? 1 : 0;
89}
90
96{
97/* Only support up to this type, if you want to extend it, make sure static void
98 * inputs_sobuffer_size_compute *inputs) padding logic is correct for the new types. */
99#define MAX_UBO_GPU_TYPE GPU_MAT4
100
101 /* Order them as mat4, vec4, vec3, vec2, float. */
103
104 /* Metal cannot pack floats after vec3. */
106 return;
107 }
108
109 /* Creates a lookup table for the different types. */
110 LinkData *inputs_lookup[MAX_UBO_GPU_TYPE + 1] = {nullptr};
111 GPUType cur_type = static_cast<GPUType>(MAX_UBO_GPU_TYPE + 1);
112
113 LISTBASE_FOREACH (LinkData *, link, inputs) {
114 GPUInput *input = (GPUInput *)link->data;
115
116 if (input->type == GPU_MAT3) {
117 /* Alignment for mat3 is not handled currently, so not supported */
118 BLI_assert_msg(0, "mat3 not supported in UBO");
119 continue;
120 }
121 if (input->type > MAX_UBO_GPU_TYPE) {
122 BLI_assert_msg(0, "GPU type not supported in UBO");
123 continue;
124 }
125
126 if (input->type == cur_type) {
127 continue;
128 }
129
130 inputs_lookup[input->type] = link;
131 cur_type = input->type;
132 }
133
134 /* If there is no GPU_VEC3 there is no need for alignment. */
135 if (inputs_lookup[GPU_VEC3] == nullptr) {
136 return;
137 }
138
139 LinkData *link = inputs_lookup[GPU_VEC3];
140 while (link != nullptr && ((GPUInput *)link->data)->type == GPU_VEC3) {
141 LinkData *link_next = link->next;
142
143 /* If GPU_VEC3 is followed by nothing or a GPU_FLOAT, no need for alignment. */
144 if ((link_next == nullptr) || ((GPUInput *)link_next->data)->type == GPU_FLOAT) {
145 break;
146 }
147
148 /* If there is a float, move it next to current vec3. */
149 if (inputs_lookup[GPU_FLOAT] != nullptr) {
150 LinkData *float_input = inputs_lookup[GPU_FLOAT];
151 inputs_lookup[GPU_FLOAT] = float_input->next;
152
153 BLI_remlink(inputs, float_input);
154 BLI_insertlinkafter(inputs, link, float_input);
155 }
156
157 link = link_next;
158 }
159#undef MAX_UBO_GPU_TYPE
160}
161
163{
164 size_t buffer_size = 0;
165 LISTBASE_FOREACH (LinkData *, link, inputs) {
166 const GPUType gputype = get_padded_gpu_type(link);
167 buffer_size += gputype * sizeof(float);
168 }
169 /* Round up to size of vec4. (Opengl Requirement) */
170 size_t alignment = sizeof(float[4]);
171 buffer_size = divide_ceil_u(buffer_size, alignment) * alignment;
172
173 return buffer_size;
174}
175
176static inline void buffer_fill_from_list(void *data, ListBase *inputs)
177{
178 /* Now that we know the total ubo size we can start populating it. */
179 float *offset = (float *)data;
180 LISTBASE_FOREACH (LinkData *, link, inputs) {
181 GPUInput *input = (GPUInput *)link->data;
182 memcpy(offset, input->vec, input->type * sizeof(float));
183 offset += get_padded_gpu_type(link);
184 }
185}
186
188
189/* -------------------------------------------------------------------- */
192
193using namespace blender::gpu;
194
196{
198 /* Direct init. */
199 if (data != nullptr) {
200 ubo->update(data);
201 }
202 else if (G.debug & G_DEBUG_GPU) {
203 /* Fill the buffer with poison values.
204 * (NaN for floats, -1 for `int` and "max value" for `uint`). */
205 blender::Vector<uchar> uninitialized_data(size, 0xFF);
206 ubo->update(uninitialized_data.data());
207 }
208 return ubo;
209}
210
212{
213 /* There is no point on creating an UBO if there is no arguments. */
215 return nullptr;
216 }
217
219 size_t buffer_size = buffer_size_from_list(inputs);
220 void *data = MEM_mallocN(buffer_size, __func__);
222
223 UniformBuf *ubo = nullptr;
224 if (buffer_size <= GPU_max_uniform_buffer_size()) {
225 ubo = GPUBackend::get()->uniformbuf_alloc(buffer_size, name);
226 /* Defer data upload. */
227 ubo->attach_data(data);
228 }
229 return ubo;
230}
231
233{
234 delete ubo;
235}
236
238{
239 ubo->update(data);
240}
241
243{
244 ubo->bind(slot);
245}
246
248{
249 ubo->bind_as_ssbo(slot);
250}
251
256
261
266
@ G_DEBUG_GPU
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void void BLI_listbase_sort(ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
MINLINE uint divide_ceil_u(uint a, uint b)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
size_t GPU_max_uniform_buffer_size()
GPUBackendType GPU_backend_get_type()
GPUType
@ GPU_VEC4
@ GPU_VEC3
@ GPU_MAT3
@ GPU_FLOAT
void GPU_uniformbuf_free(blender::gpu::UniformBuf *ubo)
void GPU_uniformbuf_bind(blender::gpu::UniformBuf *ubo, int slot)
blender::gpu::UniformBuf * GPU_uniformbuf_create_from_list(ListBase *inputs, const char *name)
void GPU_uniformbuf_bind_as_ssbo(blender::gpu::UniformBuf *ubo, int slot)
void GPU_uniformbuf_clear_to_zero(blender::gpu::UniformBuf *ubo)
void GPU_uniformbuf_unbind(blender::gpu::UniformBuf *ubo)
blender::gpu::UniformBuf * GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name)
void GPU_uniformbuf_debug_unbind_all()
void GPU_uniformbuf_update(blender::gpu::UniformBuf *ubo, const void *data)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static Context * get()
virtual void debug_unbind_all_ubo()=0
static GPUBackend * get()
virtual UniformBuf * uniformbuf_alloc(size_t size, const char *name)=0
virtual void unbind()=0
virtual void update(const void *data)=0
UniformBuf(size_t size, const char *name)
virtual void bind(int slot)=0
virtual void bind_as_ssbo(int slot)=0
virtual void clear_to_zero()=0
nullptr float
#define input
static void buffer_from_list_inputs_sort(ListBase *inputs)
static void buffer_fill_from_list(void *data, ListBase *inputs)
static int inputs_cmp(const void *a, const void *b)
static size_t buffer_size_from_list(ListBase *inputs)
#define MAX_UBO_GPU_TYPE
static GPUType get_padded_gpu_type(LinkData *link)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
#define G(x, y, z)
static blender::bke::bNodeSocketTemplate inputs[]
const char * name
void * data
struct LinkData * next