Blender V4.5
draw_curves.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BLI_utildefines.h"
12
13#include "DNA_curves_types.h"
14
15#include "BKE_attribute.hh"
16#include "BKE_curves.hh"
17#include "BKE_customdata.hh"
18
19#include "GPU_batch.hh"
20#include "GPU_capabilities.hh"
21#include "GPU_material.hh"
22#include "GPU_shader.hh"
23#include "GPU_texture.hh"
24#include "GPU_vertex_buffer.hh"
25
26#include "DRW_gpu_wrapper.hh"
27#include "DRW_render.hh"
28
29#include "draw_cache_impl.hh"
30#include "draw_common.hh"
33#include "draw_hair_private.hh"
34#include "draw_shader.hh"
35
36namespace blender::draw {
37
39
41{
43 if (used >= ubos.size()) {
44 ubos.append(std::make_unique<CurvesInfosBuf>());
45 ptr = ubos.last().get();
46 }
47 else {
48 ptr = ubos[used++].get();
49 }
50
51 memset(ptr->data(), 0, sizeof(CurvesInfos));
52 return *ptr;
53}
54
55gpu::VertBuf *CurvesModule::drw_curves_ensure_dummy_vbo()
56{
59
62
63 const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
65 GPU_vertbuf_attr_fill(vbo, dummy_id, vert);
66 /* Create vbo immediately to bind to texture buffer. */
67 GPU_vertbuf_use(vbo);
68 return vbo;
69}
70
71void DRW_curves_init(DRWData *drw_data)
72{
73 if (drw_data == nullptr) {
74 drw_data = drw_get().data;
75 }
76 if (drw_data->curves_module == nullptr) {
77 drw_data->curves_module = MEM_new<CurvesModule>("CurvesModule");
78 }
79}
80
82{
83 drw_data->curves_module->init();
84}
85
87{
88 MEM_delete(curves_module);
89}
90
92 const int curves_num,
93 gpu::VertBuf *output_buf,
94 gpu::VertBuf *input_buf)
95{
96 BLI_assert(input_buf != nullptr);
97 BLI_assert(output_buf != nullptr);
99
100 /* TODO(fclem): Remove Global access. */
102 pass.shader_set(shader);
103 pass.bind_texture("hairPointBuffer", input_buf);
104 pass.bind_texture("hairStrandBuffer", cache->proc_strand_buf);
105 pass.bind_texture("hairStrandSegBuffer", cache->proc_strand_seg_buf);
106 pass.push_constant("hairStrandsRes", &cache->final.resolution);
107 pass.bind_ssbo("posTime", output_buf);
108
109 const int max_strands_per_call = GPU_max_work_group_count(0);
110 int strands_start = 0;
111 while (strands_start < curves_num) {
112 int batch_strands_len = std::min(curves_num - strands_start, max_strands_per_call);
113 pass.push_constant("hairStrandOffset", strands_start);
114 pass.dispatch(int3(batch_strands_len, cache->final.resolution, 1));
115 strands_start += batch_strands_len;
116 }
117}
118
120{
121 const int curves_num = cache->curves_num;
122 const int final_points_len = cache->final.resolution * curves_num;
123 if (final_points_len == 0) {
124 return;
125 }
126
127 drw_curves_cache_update_compute(cache, curves_num, cache->final.proc_buf, cache->proc_point_buf);
128
129 const VectorSet<std::string> &attrs = cache->final.attr_used;
130 for (const int i : attrs.index_range()) {
131 if (!cache->proc_attributes_point_domain[i]) {
132 continue;
133 }
135 cache, curves_num, cache->final.attributes_buf[i], cache->proc_attributes_buf[i]);
136 }
137}
138
140 GPUMaterial *gpu_material,
141 int subdiv,
142 int thickness_res)
143{
144 CurvesEvalCache *cache;
146 &curves, &cache, gpu_material, subdiv, thickness_res);
147
148 if (update) {
150 }
151 return cache;
152}
153
155{
156 const DRWContext *draw_ctx = DRW_context_get();
157 const Scene *scene = draw_ctx->scene;
158
159 const int subdiv = scene->r.hair_subdiv;
160 const int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
161
163 CurvesEvalCache *cache = drw_curves_cache_get(curves, nullptr, subdiv, thickness_res);
164
165 return cache->final.proc_buf;
166}
167
168static int attribute_index_in_material(GPUMaterial *gpu_material, const StringRef name)
169{
170 if (!gpu_material) {
171 return -1;
172 }
173
174 int index = 0;
175
176 ListBase gpu_attrs = GPU_material_attributes(gpu_material);
177 LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
178 if (gpu_attr->name == name) {
179 return index;
180 }
181
182 index++;
183 }
184
185 return -1;
186}
187
189{
191
192 /* TODO(fclem): Remove Global access. */
194
195 /* NOTE: This also update legacy hairs too as they populate the same pass. */
196 manager.submit(pass);
198
199 /* Make sure calling this function again will not subdivide the same data. */
200 pass.init();
201
203}
204
205/* New Draw Manager. */
206
208 GPUMaterial *gpu_material,
209 int subdiv,
210 int thickness_res)
211{
212 CurvesEvalCache *cache;
214 &curves, &cache, gpu_material, subdiv, thickness_res);
215
216 if (!update) {
217 return cache;
218 }
219
220 const int curves_num = cache->curves_num;
221 const int final_points_len = cache->final.resolution * curves_num;
222
224
225 auto cache_update = [&](gpu::VertBuf *output_buf, gpu::VertBuf *input_buf) {
226 PassSimple::Sub &ob_ps = module.refine.sub("Object Pass");
227
229
230 ob_ps.bind_texture("hairPointBuffer", input_buf);
231 ob_ps.bind_texture("hairStrandBuffer", cache->proc_strand_buf);
232 ob_ps.bind_texture("hairStrandSegBuffer", cache->proc_strand_seg_buf);
233 ob_ps.push_constant("hairStrandsRes", &cache->final.resolution);
234 ob_ps.bind_ssbo("posTime", output_buf);
235
236 const int max_strands_per_call = GPU_max_work_group_count(0);
237 int strands_start = 0;
238 while (strands_start < curves_num) {
239 int batch_strands_len = std::min(curves_num - strands_start, max_strands_per_call);
240 PassSimple::Sub &sub_ps = ob_ps.sub("Sub Pass");
241 sub_ps.push_constant("hairStrandOffset", strands_start);
242 sub_ps.dispatch(int3(batch_strands_len, cache->final.resolution, 1));
243 strands_start += batch_strands_len;
244 }
245 };
246
247 if (final_points_len > 0) {
248 cache_update(cache->final.proc_buf, cache->proc_point_buf);
249
250 const VectorSet<std::string> &attrs = cache->final.attr_used;
251 for (const int i : attrs.index_range()) {
252 /* Only refine point attributes. */
253 if (cache->proc_attributes_point_domain[i]) {
254 cache_update(cache->final.attributes_buf[i], cache->proc_attributes_buf[i]);
255 }
256 }
257 }
258
259 return cache;
260}
261
263{
264 const int subdiv = scene->r.hair_subdiv;
265 const int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
266
268 CurvesEvalCache *cache = curves_cache_get(curves, nullptr, subdiv, thickness_res);
269
270 return cache->final.proc_buf;
271}
272
273template<typename PassT>
274gpu::Batch *curves_sub_pass_setup_implementation(PassT &sub_ps,
275 const Scene *scene,
276 Object *ob,
277 GPUMaterial *gpu_material)
278{
280
282 CurvesInfosBuf &curves_infos = module.ubo_pool.alloc();
283 BLI_assert(ob->type == OB_CURVES);
285
286 const int subdiv = scene->r.hair_subdiv;
287 const int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
288
290 curves_id, gpu_material, subdiv, thickness_res);
291
292 /* Ensure we have no unbound resources.
293 * Required for Vulkan.
294 * Fixes issues with certain GL drivers not drawing anything. */
295 sub_ps.bind_texture("u", module.dummy_vbo);
296 sub_ps.bind_texture("au", module.dummy_vbo);
297 sub_ps.bind_texture("a", module.dummy_vbo);
298 sub_ps.bind_texture("c", module.dummy_vbo);
299 sub_ps.bind_texture("ac", module.dummy_vbo);
300 if (gpu_material) {
301 ListBase attr_list = GPU_material_attributes(gpu_material);
303 for (const GPUMaterialAttribute *attr : attrs) {
304 sub_ps.bind_texture(attr->input_name, module.dummy_vbo);
305 }
306 }
307
308 /* TODO: Generalize radius implementation for curves data type. */
309 float hair_rad_shape = 0.0f;
310 float hair_rad_root = 0.005f;
311 float hair_rad_tip = 0.0f;
312 bool hair_close_tip = true;
313
314 /* Use the radius of the root and tip of the first curve for now. This is a workaround that we
315 * use for now because we can't use a per-point radius yet. */
316 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
317 if (curves.curves_num() >= 1) {
319 "radius", bke::AttrDomain::Point, 0.005f);
320 const IndexRange first_curve_points = curves.points_by_curve()[0];
321 const float first_radius = radii[first_curve_points.first()];
322 const float last_radius = radii[first_curve_points.last()];
323 const float middle_radius = radii[first_curve_points.size() / 2];
324 hair_rad_root = radii[first_curve_points.first()];
325 hair_rad_tip = radii[first_curve_points.last()];
326 hair_rad_shape = std::clamp(
327 math::safe_divide(middle_radius - first_radius, last_radius - first_radius) * 2.0f - 1.0f,
328 -1.0f,
329 1.0f);
330 }
331
332 sub_ps.bind_texture("hairPointBuffer", curves_cache->final.proc_buf);
333 if (curves_cache->proc_length_buf) {
334 sub_ps.bind_texture("l", curves_cache->proc_length_buf);
335 }
336
337 StringRef curve_data_render_uv;
338 StringRef point_data_render_uv;
340 curve_data_render_uv = CustomData_get_render_layer_name(&curves_id.geometry.curve_data,
342 }
344 point_data_render_uv = CustomData_get_render_layer_name(&curves_id.geometry.point_data,
346 }
347
348 const VectorSet<std::string> &attrs = curves_cache->final.attr_used;
349 for (const int i : attrs.index_range()) {
350 const StringRef name = attrs[i];
351 char sampler_name[32];
352 drw_curves_get_attribute_sampler_name(name, sampler_name);
353
354 if (!curves_cache->proc_attributes_point_domain[i]) {
355 if (!curves_cache->proc_attributes_buf[i]) {
356 continue;
357 }
358 sub_ps.bind_texture(sampler_name, curves_cache->proc_attributes_buf[i]);
359 if (name == curve_data_render_uv) {
360 sub_ps.bind_texture("a", curves_cache->proc_attributes_buf[i]);
361 }
362 }
363 else {
364 if (!curves_cache->final.attributes_buf[i]) {
365 continue;
366 }
367 sub_ps.bind_texture(sampler_name, curves_cache->final.attributes_buf[i]);
368 if (name == point_data_render_uv) {
369 sub_ps.bind_texture("a", curves_cache->final.attributes_buf[i]);
370 }
371 }
372
373 /* Some attributes may not be used in the shader anymore and were not garbage collected yet, so
374 * we need to find the right index for this attribute as uniforms defining the scope of the
375 * attributes are based on attribute loading order, which is itself based on the material's
376 * attributes. */
377 const int index = attribute_index_in_material(gpu_material, name);
378 if (index != -1) {
379 curves_infos.is_point_attribute[index][0] = curves_cache->proc_attributes_point_domain[i];
380 }
381 }
382
383 curves_infos.push_update();
384
385 sub_ps.bind_ubo("drw_curves", curves_infos);
386
387 sub_ps.push_constant("hairStrandsRes", &curves_cache->final.resolution, 1);
388 sub_ps.push_constant("hairThicknessRes", thickness_res);
389 sub_ps.push_constant("hairRadShape", hair_rad_shape);
390 sub_ps.push_constant("hairDupliMatrix", ob->object_to_world());
391 sub_ps.push_constant("hairRadRoot", hair_rad_root);
392 sub_ps.push_constant("hairRadTip", hair_rad_tip);
393 sub_ps.push_constant("hairCloseTip", hair_close_tip);
394
395 return curves_cache->final.proc_hairs;
396}
397
399 const Scene *scene,
400 Object *ob,
401 GPUMaterial *gpu_material)
402{
403 return curves_sub_pass_setup_implementation(ps, scene, ob, gpu_material);
404}
405
407 const Scene *scene,
408 Object *ob,
409 GPUMaterial *gpu_material)
410{
411 return curves_sub_pass_setup_implementation(ps, scene, ob, gpu_material);
412}
413
414} // namespace blender::draw
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
const char * CustomData_get_render_layer_name(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
unsigned int uint
@ CD_PROP_FLOAT2
@ OB_CURVES
@ SCE_HAIR_SHAPE_STRAND
void DRW_submission_end()
void DRW_submission_start()
int GPU_max_work_group_count(int index)
ListBase GPU_material_attributes(const GPUMaterial *material)
void GPU_memory_barrier(eGPUBarrier barrier)
Definition gpu_state.cc:385
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
void GPU_vertbuf_use(blender::gpu::VertBuf *)
void GPU_vertbuf_attr_fill(blender::gpu::VertBuf *, uint a_idx, const void *data)
blender::gpu::VertBuf * GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
@ GPU_USAGE_STATIC
@ GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
IndexRange index_range() const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
OffsetIndices< int > points_by_curve() const
AttributeAccessor attributes() const
void submit(PassSimple &pass, View &view)
void bind_texture(const char *name, GPUTexture *texture, GPUSamplerState state=sampler_auto)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:681
void dispatch(int group_len)
Definition draw_pass.hh:994
void push_constant(const char *name, const float &data)
void bind_ssbo(const char *name, GPUStorageBuf *buffer)
void shader_set(GPUShader *shader)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:490
const DRWContext * DRW_context_get()
Mesh & DRW_object_get_data_for_drawing(const Object &object)
DRWContext & drw_get()
GPUShader * DRW_shader_curves_refine_get(blender::draw::CurvesEvalShader)
format
void DRW_curves_module_free(draw::CurvesModule *module)
static CurvesEvalCache * drw_curves_cache_get(Curves &curves, GPUMaterial *gpu_material, int subdiv, int thickness_res)
gpu::VertBuf * curves_pos_buffer_get(Scene *scene, Object *object)
static CurvesEvalCache * curves_cache_get(Curves &curves, GPUMaterial *gpu_material, int subdiv, int thickness_res)
detail::Pass< command::DrawCommandBuf > PassSimple
gpu::Batch * curves_sub_pass_setup(PassMain::Sub &ps, const Scene *scene, Object *ob, GPUMaterial *gpu_material=nullptr)
static void drw_curves_cache_update_compute(CurvesEvalCache *cache, const int curves_num, gpu::VertBuf *output_buf, gpu::VertBuf *input_buf)
void DRW_curves_init(DRWData *drw_data=nullptr)
static int attribute_index_in_material(GPUMaterial *gpu_material, const StringRef name)
void DRW_curves_update(draw::Manager &manager)
void drw_curves_get_attribute_sampler_name(const StringRef layer_name, char r_sampler_name[32])
gpu::Batch * curves_sub_pass_setup_implementation(PassT &sub_ps, const Scene *scene, Object *ob, GPUMaterial *gpu_material)
void DRW_curves_begin_sync(DRWData *drw_data)
bool curves_ensure_procedural_data(Curves *curves_id, CurvesEvalCache **r_cache, const GPUMaterial *gpu_material, const int subdiv, const int thickness_res)
UniformBuffer< CurvesInfos > CurvesInfosBuf
gpu::VertBuf * DRW_curves_pos_buffer_get(Object *object)
T safe_divide(const T &a, const T &b)
VecBase< int32_t, 3 > int3
ListBaseWrapperTemplate< ListBase, T > ListBaseWrapper
static void update(bNodeTree *ntree)
static struct PyModuleDef module
Definition python.cpp:796
CustomData point_data
CustomData curve_data
uint4 is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX]
CurvesGeometry geometry
DRWData * data
Scene * scene
blender::draw::CurvesModule * curves_module
struct RenderData r
gpu::VertBuf * proc_attributes_buf[GPU_MAX_ATTR]
std::array< bool, GPU_MAX_ATTR > proc_attributes_point_domain
gpu::VertBuf * attributes_buf[GPU_MAX_ATTR]
Vector< std::unique_ptr< CurvesInfosBuf > > ubos
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4226