Blender V5.0
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"
29#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 ResourceHandleRange resource_handle = {};
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(inst, 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 ResourceHandleRange 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 data.pos_buf = draw::hair_pos_buffer_get(inst_.scene, ob, particle_sys, modifier_data);
182 return data;
183 }
184 switch (ob->type) {
185 case OB_CURVES:
186 data.pos_buf = draw::curves_pos_buffer_get(ob);
187 break;
188 case OB_POINTCLOUD:
190 break;
191 case OB_MESH:
192 data.pos_buf = DRW_cache_mesh_surface_get(ob);
193 break;
194 }
195 return data;
196 };
197
198 const VelocityGeometryData &data = geometry_map.lookup_or_add_cb(vel.id, add_cb);
199
200 if (!data.has_data()) {
201 has_deform = false;
202 }
203 }
204
205 /* Avoid drawing object that has no motions but were tagged as such. */
206 if (step_ == STEP_CURRENT && has_motion == true && has_deform == false) {
207 const float4x4 &obmat_curr = (*object_steps[STEP_CURRENT])[vel.obj.ofs[STEP_CURRENT]];
208 const float4x4 &obmat_prev = (*object_steps[STEP_PREVIOUS])[vel.obj.ofs[STEP_PREVIOUS]];
209 if (inst_.is_viewport()) {
210 has_motion = (obmat_curr != obmat_prev);
211 }
212 else {
213 const float4x4 &obmat_next = (*object_steps[STEP_NEXT])[vel.obj.ofs[STEP_NEXT]];
214 has_motion = (obmat_curr != obmat_prev || obmat_curr != obmat_next);
215 }
216 }
217
218#if 0
219 if (!has_motion && !has_deform) {
220 std::cout << "Detected no motion on " << ob->id.name << std::endl;
221 }
222 if (has_deform) {
223 std::cout << "Geometry Motion on " << ob->id.name << std::endl;
224 }
225 if (has_motion) {
226 std::cout << "Object Motion on " << ob->id.name << std::endl;
227 }
228#endif
229
230 if (!has_motion && !has_deform) {
231 return false;
232 }
233
234 return true;
235}
236
238{
239 uint dst_ofs = 0;
240 for (VelocityGeometryData &geom : geometry_map.values()) {
241 gpu::VertBuf *pos_buf = geom.pos_buf_get();
242 if (!pos_buf) {
243 continue;
244 }
245 uint src_len = GPU_vertbuf_get_vertex_len(pos_buf);
246 geom.len = src_len;
247 geom.ofs = dst_ofs;
248 dst_ofs += src_len;
249 }
250 /* TODO(@fclem): Fail gracefully (disable motion blur + warning print) if
251 * `tot_len * sizeof(float4)` is greater than max SSBO size. */
252 geometry_steps[step_]->resize(max_ii(16, dst_ofs));
253
255
256 PassSimple copy_ps("Velocity Copy Pass");
257 copy_ps.init();
259 copy_ps.shader_set(inst_.shaders.static_shader_get(VERTEX_COPY));
260 copy_ps.bind_ssbo("out_buf", *geometry_steps[step_]);
261
262 for (VelocityGeometryData &geom : geometry_map.values()) {
263 gpu::VertBuf *pos_buf = geom.pos_buf_get();
264 if (!pos_buf || geom.len == 0) {
265 continue;
266 }
268 if (format->stride == 16) {
270 pos_buf,
271 geom.ofs * sizeof(float4),
272 0,
273 geom.len * sizeof(float4));
274 }
275 else {
276 BLI_assert(format->stride % 4 == 0);
277 copy_ps.bind_ssbo("in_buf", pos_buf);
278 copy_ps.push_constant("start_offset", geom.ofs);
279 copy_ps.push_constant("vertex_stride", int(format->stride / 4));
280 copy_ps.push_constant("vertex_count", geom.len);
281 uint group_len_x = divide_ceil_u(geom.len, VERTEX_COPY_GROUP_SIZE);
282 uint verts_per_thread = divide_ceil_u(group_len_x, GPU_max_work_group_count(0));
283 copy_ps.dispatch(int3(group_len_x / verts_per_thread, 1, 1));
284 }
285 }
286
288 inst_.manager->submit(copy_ps);
289
291
292 /* Copy back the #VelocityGeometryIndex into #VelocityObjectData which are
293 * indexed using persistent keys (unlike geometries which are indexed by volatile ID). */
294 for (VelocityObjectData &vel : velocity_map.values()) {
295 const VelocityGeometryData &geom = geometry_map.lookup_default(vel.id, VelocityGeometryData());
296 vel.geo.len[step_] = geom.len;
297 vel.geo.ofs[step_] = geom.ofs;
298 /* Avoid reuse. */
299 vel.id = 0;
300 }
301
302 geometry_map.clear();
303}
304
306{
307 auto swap_steps = [&](eVelocityStep step_a, eVelocityStep step_b) {
308 std::swap(object_steps[step_a], object_steps[step_b]);
309 std::swap(geometry_steps[step_a], geometry_steps[step_b]);
310 std::swap(camera_steps[step_a], camera_steps[step_b]);
311 std::swap(step_time[step_a], step_time[step_b]);
312 std::swap(object_steps_usage[step_a], object_steps_usage[step_b]);
313
314 for (VelocityObjectData &vel : velocity_map.values()) {
315 vel.obj.ofs[step_a] = vel.obj.ofs[step_b];
316 vel.obj.ofs[step_b] = uint(-1);
317 vel.geo.ofs[step_a] = vel.geo.ofs[step_b];
318 vel.geo.len[step_a] = vel.geo.len[step_b];
319 vel.geo.ofs[step_b] = uint(-1);
320 vel.geo.len[step_b] = uint(-1);
321 }
322 };
323
324 if (inst_.is_viewport()) {
326 /* For viewport we only use the last rendered redraw as previous frame.
327 * We swap current with previous step at the end of a redraw.
328 * We do not support motion blur as it is rendered to avoid conflicting motions
329 * for temporal reprojection. */
331 }
332 else {
333 /* Render case: The STEP_CURRENT is left untouched. */
335 }
336}
337
339{
340 step_ = STEP_CURRENT;
342 object_steps_usage[step_] = 0;
343
344 /* STEP_NEXT is not used for viewport. (See #131134) */
345 BLI_assert(!inst_.is_viewport() || object_steps_usage[STEP_NEXT] == 0);
346}
347
349{
350 Vector<ObjectKey, 0> deleted_obj;
351
352 uint32_t max_resource_id_ = 0u;
353
355 if (item.value.obj.resource_id == uint32_t(-1)) {
356 deleted_obj.append(item.key);
357 }
358 else {
359 max_resource_id_ = max_uu(max_resource_id_, item.value.obj.resource_id);
360 }
361 }
362
363 for (auto &key : deleted_obj) {
364 velocity_map.remove(key);
365 }
366
367 indirection_buf.resize(ceil_to_multiple_u(max_resource_id_ + 1, 128));
368
369 /* Avoid uploading more data to the GPU as well as an extra level of
370 * indirection on the GPU by copying back offsets the to VelocityIndex. */
371 for (VelocityObjectData &vel : velocity_map.values()) {
372 /* Disable deform if vertex count mismatch. */
373 if (inst_.is_viewport()) {
374 /* Current geometry step will be copied at the end of the frame.
375 * Thus vel.geo.len[STEP_CURRENT] is not yet valid and the current length is manually
376 * retrieved. */
377 gpu::VertBuf *pos_buf =
378 geometry_map.lookup_default(vel.id, VelocityGeometryData()).pos_buf_get();
379 vel.geo.do_deform = pos_buf != nullptr &&
380 (vel.geo.len[STEP_PREVIOUS] == GPU_vertbuf_get_vertex_len(pos_buf));
381 }
382 else {
383 vel.geo.do_deform = (vel.geo.len[STEP_CURRENT] != 0) &&
384 (vel.geo.len[STEP_CURRENT] == vel.geo.len[STEP_PREVIOUS]) &&
385 (vel.geo.len[STEP_CURRENT] == vel.geo.len[STEP_NEXT]);
386 }
387 indirection_buf[vel.obj.resource_id] = vel;
388 /* Reset for next sync. */
389 vel.obj.resource_id = uint(-1);
390 }
391
392 object_steps[STEP_PREVIOUS]->push_update();
393 object_steps[STEP_NEXT]->push_update();
394 camera_steps[STEP_PREVIOUS]->push_update();
395 camera_steps[STEP_CURRENT]->push_update();
396 camera_steps[STEP_NEXT]->push_update();
397 indirection_buf.push_update();
398}
399
400bool VelocityModule::object_has_velocity(const Object *ob)
401{
402#if 0
403 RigidBodyOb *rbo = ob->rigidbody_object;
404 /* Active rigidbody objects only, as only those are affected by sim. */
405 const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
406 /* For now we assume dupli objects are moving. */
407 const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
408 const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true);
409#else
410 UNUSED_VARS(ob);
411 /* BKE_object_moves_in_time does not work in some cases.
412 * Better detect non moving object after evaluation. */
413 const bool object_moves = true;
414#endif
415 return object_moves;
416}
417
418bool VelocityModule::object_is_deform(const Object *ob)
419{
420 RigidBodyOb *rbo = ob->rigidbody_object;
421 /* Active rigidbody objects only, as only those are affected by sim. */
422 const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
423 const bool is_deform = BKE_object_is_deform_modified(inst_.scene, (Object *)ob) ||
424 (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0);
425
426 return is_deform;
427}
428
430{
431 /* Only valid after sync. */
432 if (inst_.is_viewport()) {
433 /* Viewport has no next step. */
435 }
438}
439
441{
442 /* Only valid after sync. */
443 if (inst_.is_viewport()) {
444 return camera_steps[STEP_PREVIOUS]->type != camera_steps[STEP_CURRENT]->type;
445 }
446 /* Cannot happen in render mode since we set the type during the init phase. */
447 return false;
448}
449
451{
452 return step_time[end] - step_time[start];
453}
454
456
457} // 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:1054
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ 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(blender::gpu::StorageBuf *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 shader_set(gpu::Shader *shader)
void dispatch(int group_len)
void state_set(DRWState state, int clip_plane_count=0)
void barrier(GPUBarrier type)
void push_constant(const char *name, const float &data)
void bind_ssbo(const char *name, gpu::StorageBuf *buffer)
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
std::array< VelocityObjectBuf *, 3 > object_steps
std::array< VelocityGeometryBuf *, 3 > geometry_steps
bool step_object_sync(ObjectKey &object_key, const ObjectRef &object_ref, int recalc, ResourceHandleRange resource_handle, ModifierData *modifier_data=nullptr, ParticleSystem *particle_sys=nullptr)
void step_sync(eVelocityStep step, float time)
Map< uint64_t, VelocityGeometryData > geometry_map
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
detail::Pass< command::DrawCommandBuf > PassSimple
gpu::VertBuf * curves_pos_buffer_get(Object *object)
gpu::VertBuf * hair_pos_buffer_get(Scene *scene, Object *object, ParticleSystem *psys, ModifierData *md)
Definition draw_hair.cc:36
gpu::Batch * DRW_cache_mesh_surface_get(Object *ob)
gpu::VertBuf * DRW_pointcloud_position_and_radius_buffer_get(Object *ob)
static void step_object_sync_render(Instance &inst, ObjectRef &ob_ref)
void foreach_hair_particle_handle(Instance &inst, 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[258]
Definition DNA_ID.h:432
short base_flag
struct RigidBodyOb * rigidbody_object
struct RenderData r