Blender V4.5
eevee_velocity.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
13
14#include "BKE_duplilist.hh"
15#include "BKE_object.hh"
16#include "BLI_map.hh"
18#include "DNA_modifier_types.h"
19#include "DNA_particle_types.h"
20#include "DNA_rigidbody_types.h"
21
22#include "DRW_engine.hh"
23#include "draw_cache.hh"
24#include "draw_cache_impl.hh"
25
26#include "eevee_instance.hh"
27// #include "eevee_renderpasses.hh"
28#include "eevee_shader.hh"
30#include "eevee_velocity.hh"
31
32#include "draw_common.hh"
33
34namespace blender::eevee {
35
36/* -------------------------------------------------------------------- */
40
42{
43 if (!inst_.is_viewport() && !inst_.is_baking() &&
44 (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR) &&
45 !inst_.motion_blur.postfx_enabled())
46 {
47 /* No motion blur and the vector pass was requested. Do the steps sync here. */
48 const Scene *scene = inst_.scene;
49 float initial_time = scene->r.cfra + scene->r.subframe;
50 step_sync(STEP_PREVIOUS, initial_time - 1.0f);
51 step_sync(STEP_NEXT, initial_time + 1.0f);
52 /* Let the main sync loop handle the current step. */
53 inst_.set_time(initial_time);
54 step_ = STEP_CURRENT;
55 }
56
57 /* For viewport, only previous motion is supported.
58 * Still bind previous step to avoid undefined behavior. */
59 next_step_ = (inst_.is_viewport() || inst_.is_baking()) ? STEP_PREVIOUS : STEP_NEXT;
60}
61
62/* Similar to Instance::object_sync, but only syncs velocity. */
63static void step_object_sync_render(Instance &inst, ObjectRef &ob_ref)
64{
65 Object *ob = ob_ref.object;
66
67 const bool is_velocity_type = ELEM(ob->type, OB_CURVES, OB_MESH, OB_POINTCLOUD);
68 const int ob_visibility = DRW_object_visibility_in_active_context(ob);
69 const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
70 (ob->type == OB_MESH);
71 const bool object_is_visible = DRW_object_is_renderable(ob) &&
72 (ob_visibility & OB_VISIBLE_SELF) != 0;
73
74 if (!is_velocity_type || (!partsys_is_visible && !object_is_visible)) {
75 return;
76 }
77
78 /* NOTE: Dummy resource handle since this won't be used for drawing. */
79 ResourceHandle resource_handle(0);
80 ObjectHandle &ob_handle = inst.sync.sync_object(ob_ref);
81
82 if (partsys_is_visible) {
83 auto sync_hair = [&](ObjectHandle hair_handle,
84 ModifierData &md,
85 ParticleSystem &particle_sys) {
87 hair_handle.object_key, ob_ref, hair_handle.recalc, resource_handle, &md, &particle_sys);
88 };
89 foreach_hair_particle_handle(ob_ref, ob_handle, sync_hair);
90 };
91
92 if (object_is_visible) {
94 ob_handle.object_key, ob_ref, ob_handle.recalc, resource_handle);
95 }
96}
97
99{
100 inst_.set_time(time);
101 step_ = step;
102 object_steps_usage[step_] = 0;
104
105 DRW_render_object_iter(inst_.render,
106 inst_.depsgraph,
107 [&](blender::draw::ObjectRef &ob_ref, RenderEngine *, Depsgraph *) {
108 step_object_sync_render(inst_, ob_ref);
109 });
110
112}
113
115{
116 inst_.camera.sync();
117 *camera_steps[step_] = inst_.camera.data_get();
118 step_time[step_] = inst_.scene->r.cfra + inst_.scene->r.subframe;
119 /* Fix undefined camera steps when rendering is starting. */
120 if ((step_ == STEP_CURRENT) && (camera_steps[STEP_PREVIOUS]->initialized == false)) {
121 *camera_steps[STEP_PREVIOUS] = *static_cast<CameraData *>(camera_steps[step_]);
122 camera_steps[STEP_PREVIOUS]->initialized = true;
124 }
125}
126
128 const ObjectRef &object_ref,
129 int /*IDRecalcFlag*/ recalc,
130 ResourceHandle resource_handle,
131 ModifierData *modifier_data /*=nullptr*/,
132 ParticleSystem *particle_sys /*=nullptr*/)
133{
134 Object *ob = object_ref.object;
135 bool has_motion = object_has_velocity(ob) || (recalc & ID_RECALC_TRANSFORM);
136 /* NOTE: Fragile. This will only work with 1 frame of lag since we can't record every geometry
137 * just in case there might be an update the next frame. */
138 bool has_deform = object_is_deform(ob) || (recalc & ID_RECALC_GEOMETRY);
139
140 if (!has_motion && !has_deform) {
141 return false;
142 }
143
144 /* Object motion. */
145 /* FIXME(fclem) As we are using original objects pointers, there is a chance the previous
146 * object key matches a totally different object if the scene was changed by user or python
147 * callback. In this case, we cannot correctly match objects between updates.
148 * What this means is that there will be incorrect motion vectors for these objects.
149 * We live with that until we have a correct way of identifying new objects. */
150 VelocityObjectData &vel = velocity_map.lookup_or_add_default(object_key);
151 vel.obj.ofs[step_] = object_steps_usage[step_]++;
152 vel.obj.resource_id = resource_handle.resource_index();
153 /* While VelocityObjectData is unique for each object/instance, multiple VelocityObjectDatas can
154 * point to the same offset in VelocityGeometryData, since geometry is stored local space. */
155 vel.id = particle_sys ? uint64_t(particle_sys) : uint64_t(ob->data);
156 object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = ob->object_to_world();
157 if (step_ == STEP_CURRENT) {
158 /* Replace invalid steps. Can happen if object was hidden in one of those steps. */
159 if (vel.obj.ofs[STEP_PREVIOUS] == -1) {
161 object_steps[STEP_PREVIOUS]->get_or_resize(
162 vel.obj.ofs[STEP_PREVIOUS]) = ob->object_to_world();
163 }
164 if (vel.obj.ofs[STEP_NEXT] == -1) {
165 if (inst_.is_viewport()) {
166 /* Just set it to 0. motion.next is not meant to be valid in the viewport. */
167 vel.obj.ofs[STEP_NEXT] = 0;
168 }
169 else {
171 object_steps[STEP_NEXT]->get_or_resize(vel.obj.ofs[STEP_NEXT]) = ob->object_to_world();
172 }
173 }
174 }
175
176 /* Geometry motion. */
177 if (has_deform) {
178 auto add_cb = [&]() {
180 if (particle_sys) {
181 if (inst_.is_viewport()) {
182 data.pos_buf = DRW_hair_pos_buffer_get(ob, particle_sys, modifier_data);
183 }
184 else {
185 data.pos_buf = draw::hair_pos_buffer_get(inst_.scene, ob, particle_sys, modifier_data);
186 }
187 return data;
188 }
189 switch (ob->type) {
190 case OB_CURVES:
191 if (inst_.is_viewport()) {
192 data.pos_buf = DRW_curves_pos_buffer_get(ob);
193 }
194 else {
195 data.pos_buf = draw::curves_pos_buffer_get(inst_.scene, ob);
196 }
197 break;
198 case OB_POINTCLOUD:
200 break;
201 case OB_MESH:
202 data.pos_buf = DRW_cache_mesh_surface_get(ob);
203 break;
204 }
205 return data;
206 };
207
208 const VelocityGeometryData &data = geometry_map.lookup_or_add_cb(vel.id, add_cb);
209
210 if (!data.has_data()) {
211 has_deform = false;
212 }
213 }
214
215 /* Avoid drawing object that has no motions but were tagged as such. */
216 if (step_ == STEP_CURRENT && has_motion == true && has_deform == false) {
217 const float4x4 &obmat_curr = (*object_steps[STEP_CURRENT])[vel.obj.ofs[STEP_CURRENT]];
218 const float4x4 &obmat_prev = (*object_steps[STEP_PREVIOUS])[vel.obj.ofs[STEP_PREVIOUS]];
219 if (inst_.is_viewport()) {
220 has_motion = (obmat_curr != obmat_prev);
221 }
222 else {
223 const float4x4 &obmat_next = (*object_steps[STEP_NEXT])[vel.obj.ofs[STEP_NEXT]];
224 has_motion = (obmat_curr != obmat_prev || obmat_curr != obmat_next);
225 }
226 }
227
228#if 0
229 if (!has_motion && !has_deform) {
230 std::cout << "Detected no motion on " << ob->id.name << std::endl;
231 }
232 if (has_deform) {
233 std::cout << "Geometry Motion on " << ob->id.name << std::endl;
234 }
235 if (has_motion) {
236 std::cout << "Object Motion on " << ob->id.name << std::endl;
237 }
238#endif
239
240 if (!has_motion && !has_deform) {
241 return false;
242 }
243
244 return true;
245}
246
248{
249 uint dst_ofs = 0;
250 for (VelocityGeometryData &geom : geometry_map.values()) {
251 gpu::VertBuf *pos_buf = geom.pos_buf_get();
252 if (!pos_buf) {
253 continue;
254 }
255 uint src_len = GPU_vertbuf_get_vertex_len(pos_buf);
256 geom.len = src_len;
257 geom.ofs = dst_ofs;
258 dst_ofs += src_len;
259 }
260 /* TODO(@fclem): Fail gracefully (disable motion blur + warning print) if
261 * `tot_len * sizeof(float4)` is greater than max SSBO size. */
262 geometry_steps[step_]->resize(max_ii(16, dst_ofs));
263
265
266 PassSimple copy_ps("Velocity Copy Pass");
267 copy_ps.init();
269 copy_ps.shader_set(inst_.shaders.static_shader_get(VERTEX_COPY));
270 copy_ps.bind_ssbo("out_buf", *geometry_steps[step_]);
271
272 for (VelocityGeometryData &geom : geometry_map.values()) {
273 gpu::VertBuf *pos_buf = geom.pos_buf_get();
274 if (!pos_buf || geom.len == 0) {
275 continue;
276 }
278 if (format->stride == 16) {
280 pos_buf,
281 geom.ofs * sizeof(float4),
282 0,
283 geom.len * sizeof(float4));
284 }
285 else {
286 BLI_assert(format->stride % 4 == 0);
287 copy_ps.bind_ssbo("in_buf", pos_buf);
288 copy_ps.push_constant("start_offset", geom.ofs);
289 copy_ps.push_constant("vertex_stride", int(format->stride / 4));
290 copy_ps.push_constant("vertex_count", geom.len);
291 uint group_len_x = divide_ceil_u(geom.len, VERTEX_COPY_GROUP_SIZE);
292 uint verts_per_thread = divide_ceil_u(group_len_x, GPU_max_work_group_count(0));
293 copy_ps.dispatch(int3(group_len_x / verts_per_thread, 1, 1));
294 }
295 }
296
298 inst_.manager->submit(copy_ps);
299
301
302 /* Copy back the #VelocityGeometryIndex into #VelocityObjectData which are
303 * indexed using persistent keys (unlike geometries which are indexed by volatile ID). */
304 for (VelocityObjectData &vel : velocity_map.values()) {
305 const VelocityGeometryData &geom = geometry_map.lookup_default(vel.id, VelocityGeometryData());
306 vel.geo.len[step_] = geom.len;
307 vel.geo.ofs[step_] = geom.ofs;
308 /* Avoid reuse. */
309 vel.id = 0;
310 }
311
312 geometry_map.clear();
313}
314
316{
317 auto swap_steps = [&](eVelocityStep step_a, eVelocityStep step_b) {
318 std::swap(object_steps[step_a], object_steps[step_b]);
319 std::swap(geometry_steps[step_a], geometry_steps[step_b]);
320 std::swap(camera_steps[step_a], camera_steps[step_b]);
321 std::swap(step_time[step_a], step_time[step_b]);
322 std::swap(object_steps_usage[step_a], object_steps_usage[step_b]);
323
324 for (VelocityObjectData &vel : velocity_map.values()) {
325 vel.obj.ofs[step_a] = vel.obj.ofs[step_b];
326 vel.obj.ofs[step_b] = uint(-1);
327 vel.geo.ofs[step_a] = vel.geo.ofs[step_b];
328 vel.geo.len[step_a] = vel.geo.len[step_b];
329 vel.geo.ofs[step_b] = uint(-1);
330 vel.geo.len[step_b] = uint(-1);
331 }
332 };
333
334 if (inst_.is_viewport()) {
336 /* For viewport we only use the last rendered redraw as previous frame.
337 * We swap current with previous step at the end of a redraw.
338 * We do not support motion blur as it is rendered to avoid conflicting motions
339 * for temporal reprojection. */
341 }
342 else {
343 /* Render case: The STEP_CURRENT is left untouched. */
345 }
346}
347
349{
350 step_ = STEP_CURRENT;
352 object_steps_usage[step_] = 0;
353
354 /* STEP_NEXT is not used for viewport. (See #131134) */
355 BLI_assert(!inst_.is_viewport() || object_steps_usage[STEP_NEXT] == 0);
356}
357
359{
360 Vector<ObjectKey, 0> deleted_obj;
361
362 uint32_t max_resource_id_ = 0u;
363
365 if (item.value.obj.resource_id == uint32_t(-1)) {
366 deleted_obj.append(item.key);
367 }
368 else {
369 max_resource_id_ = max_uu(max_resource_id_, item.value.obj.resource_id);
370 }
371 }
372
373 for (auto &key : deleted_obj) {
374 velocity_map.remove(key);
375 }
376
377 indirection_buf.resize(ceil_to_multiple_u(max_resource_id_ + 1, 128));
378
379 /* Avoid uploading more data to the GPU as well as an extra level of
380 * indirection on the GPU by copying back offsets the to VelocityIndex. */
381 for (VelocityObjectData &vel : velocity_map.values()) {
382 /* Disable deform if vertex count mismatch. */
383 if (inst_.is_viewport()) {
384 /* Current geometry step will be copied at the end of the frame.
385 * Thus vel.geo.len[STEP_CURRENT] is not yet valid and the current length is manually
386 * retrieved. */
387 gpu::VertBuf *pos_buf =
388 geometry_map.lookup_default(vel.id, VelocityGeometryData()).pos_buf_get();
389 vel.geo.do_deform = pos_buf != nullptr &&
390 (vel.geo.len[STEP_PREVIOUS] == GPU_vertbuf_get_vertex_len(pos_buf));
391 }
392 else {
393 vel.geo.do_deform = (vel.geo.len[STEP_CURRENT] != 0) &&
394 (vel.geo.len[STEP_CURRENT] == vel.geo.len[STEP_PREVIOUS]) &&
395 (vel.geo.len[STEP_CURRENT] == vel.geo.len[STEP_NEXT]);
396 }
397 indirection_buf[vel.obj.resource_id] = vel;
398 /* Reset for next sync. */
399 vel.obj.resource_id = uint(-1);
400 }
401
402 object_steps[STEP_PREVIOUS]->push_update();
403 object_steps[STEP_NEXT]->push_update();
404 camera_steps[STEP_PREVIOUS]->push_update();
405 camera_steps[STEP_CURRENT]->push_update();
406 camera_steps[STEP_NEXT]->push_update();
407 indirection_buf.push_update();
408}
409
410bool VelocityModule::object_has_velocity(const Object *ob)
411{
412#if 0
413 RigidBodyOb *rbo = ob->rigidbody_object;
414 /* Active rigidbody objects only, as only those are affected by sim. */
415 const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
416 /* For now we assume dupli objects are moving. */
417 const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
418 const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true);
419#else
420 UNUSED_VARS(ob);
421 /* BKE_object_moves_in_time does not work in some cases.
422 * Better detect non moving object after evaluation. */
423 const bool object_moves = true;
424#endif
425 return object_moves;
426}
427
428bool VelocityModule::object_is_deform(const Object *ob)
429{
430 RigidBodyOb *rbo = ob->rigidbody_object;
431 /* Active rigidbody objects only, as only those are affected by sim. */
432 const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
433 const bool is_deform = BKE_object_is_deform_modified(inst_.scene, (Object *)ob) ||
434 (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0);
435
436 return is_deform;
437}
438
440{
441 /* Only valid after sync. */
442 if (inst_.is_viewport()) {
443 /* Viewport has no next step. */
445 }
448}
449
451{
452 /* Only valid after sync. */
453 if (inst_.is_viewport()) {
454 return camera_steps[STEP_PREVIOUS]->type != camera_steps[STEP_CURRENT]->type;
455 }
456 /* Cannot happen in render mode since we set the type during the init phase. */
457 return false;
458}
459
461{
462 return step_time[end] - step_time[start];
463}
464
466
467} // namespace blender::eevee
General operations, lookup, etc. for blender objects.
@ OB_VISIBLE_SELF
@ OB_VISIBLE_PARTICLES
int BKE_object_is_deform_modified(Scene *scene, Object *ob)
bool BKE_object_moves_in_time(const Object *object, bool recurse_parent)
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE uint ceil_to_multiple_u(uint a, uint b)
MINLINE uint divide_ceil_u(uint a, uint b)
MINLINE uint max_uu(uint a, uint b)
MINLINE int max_ii(int a, int b)
unsigned int uint
#define UNUSED_VARS(...)
#define ELEM(...)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:962
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ BASE_FROM_DUPLI
@ EEVEE_RENDER_PASS_VECTOR
@ OB_MESH
@ OB_POINTCLOUD
@ OB_CURVES
Types and defines for representing Rigid Body entities.
@ RBO_TYPE_ACTIVE
@ RBO_FLAG_USE_DEFORM
void DRW_submission_end()
void DRW_submission_start()
int GPU_max_work_group_count(int index)
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
void GPU_storagebuf_copy_sub_from_vertbuf(GPUStorageBuf *ssbo, blender::gpu::VertBuf *src, uint dst_offset, uint src_offset, uint copy_size)
Copy a part of a vertex buffer to a storage buffer.
const GPUVertFormat * GPU_vertbuf_get_format(const blender::gpu::VertBuf *verts)
uint GPU_vertbuf_get_vertex_len(const blender::gpu::VertBuf *verts)
BMesh const char void * data
unsigned long long int uint64_t
void append(const T &value)
void dispatch(int group_len)
Definition draw_pass.hh:994
void barrier(eGPUBarrier type)
void state_set(DRWState state, int clip_plane_count=0)
void push_constant(const char *name, const float &data)
void bind_ssbo(const char *name, GPUStorageBuf *buffer)
void shader_set(GPUShader *shader)
A running instance of the engine.
ObjectHandle & sync_object(const ObjectRef &ob_ref)
Definition eevee_sync.cc:32
float step_time_delta_get(eVelocityStep start, eVelocityStep end) const
Map< ObjectKey, VelocityObjectData > velocity_map
std::array< CameraDataBuf *, 3 > camera_steps
bool step_object_sync(ObjectKey &object_key, const ObjectRef &object_ref, int recalc, ResourceHandle resource_handle, ModifierData *modifier_data=nullptr, ParticleSystem *particle_sys=nullptr)
std::array< VelocityObjectBuf *, 3 > object_steps
std::array< VelocityGeometryBuf *, 3 > geometry_steps
void step_sync(eVelocityStep step, float time)
Map< uint64_t, VelocityGeometryData > geometry_map
blender::gpu::VertBuf * DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, ModifierData *md)
Definition draw_hair.cc:85
void DRW_render_object_iter(RenderEngine *engine, Depsgraph *depsgraph, std::function< void(blender::draw::ObjectRef &, RenderEngine *, Depsgraph *)> callback)
bool DRW_object_is_renderable(const Object *ob)
int DRW_object_visibility_in_active_context(const Object *ob)
@ DRW_STATE_NO_DRAW
Definition draw_state.hh:27
#define VERTEX_COPY_GROUP_SIZE
static bool initialized
format
gpu::VertBuf * curves_pos_buffer_get(Scene *scene, Object *object)
detail::Pass< command::DrawCommandBuf > PassSimple
gpu::VertBuf * hair_pos_buffer_get(Scene *scene, Object *object, ParticleSystem *psys, ModifierData *md)
Definition draw_hair.cc:179
gpu::Batch * DRW_cache_mesh_surface_get(Object *ob)
gpu::VertBuf * DRW_pointcloud_position_and_radius_buffer_get(Object *ob)
gpu::VertBuf * DRW_curves_pos_buffer_get(Object *object)
static void step_object_sync_render(Instance &inst, ObjectRef &ob_ref)
void foreach_hair_particle_handle(ObjectRef &ob_ref, ObjectHandle ob_handle, HairHandleCallback callback)
T step(const T &edge, const T &value)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 3 > int3
char name[66]
Definition DNA_ID.h:415
short base_flag
struct RigidBodyOb * rigidbody_object
struct RenderData r