EEVEE-Next: Fix Hair and Curves motion vectors #112425

Merged
Miguel Pozo merged 5 commits from pragma37/blender:pull-fix-curves-motion-blur into main 2023-09-26 19:55:54 +02:00
6 changed files with 287 additions and 44 deletions

View File

@ -25,6 +25,8 @@
#include "DNA_particle_types.h"
#include "draw_common.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
@ -291,9 +293,20 @@ void Instance::render_sync()
manager->begin_sync();
draw::hair_init();
draw::curves_init();
begin_sync();
DRW_render_object_iter(this, render, depsgraph, object_sync_render);
draw::hair_update(*manager);
draw::curves_update(*manager);
draw::hair_free();
draw::curves_free();
velocity.geometry_steps_fill();
end_sync();
manager->end_sync();

View File

@ -27,6 +27,8 @@
#include "eevee_shader_shared.hh"
#include "eevee_velocity.hh"
#include "draw_common.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
@ -100,7 +102,17 @@ void VelocityModule::step_sync(eVelocityStep step, float time)
step_ = step;
object_steps_usage[step_] = 0;
step_camera_sync();
draw::hair_init();
draw::curves_init();
DRW_render_object_iter(&inst_, inst_.render, inst_.depsgraph, step_object_sync_render);
draw::hair_update(*inst_.manager);
draw::curves_update(*inst_.manager);
draw::hair_free();
draw::curves_free();
geometry_steps_fill();
}
@ -142,7 +154,7 @@ bool VelocityModule::step_object_sync(Object *ob,
VelocityObjectData &vel = velocity_map.lookup_or_add_default(object_key);
vel.obj.ofs[step_] = object_steps_usage[step_]++;
vel.obj.resource_id = resource_handle.resource_index();
vel.id = particle_sys ? &particle_sys->part->id : &ob->id;
vel.id = object_key.hash_value;
object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = float4x4_view(ob->object_to_world);
if (step_ == STEP_CURRENT) {
/* Replace invalid steps. Can happen if object was hidden in one of those steps. */
@ -163,12 +175,22 @@ bool VelocityModule::step_object_sync(Object *ob,
auto add_cb = [&]() {
VelocityGeometryData data;
if (particle_sys) {
data.pos_buf = DRW_hair_pos_buffer_get(ob, particle_sys, modifier_data);
if (inst_.is_viewport()) {
data.pos_buf = DRW_hair_pos_buffer_get(ob, particle_sys, modifier_data);
}
else {
data.pos_buf = draw::hair_pos_buffer_get(inst_.scene, ob, particle_sys, modifier_data);
}
return data;
}
switch (ob->type) {
case OB_CURVES:
data.pos_buf = DRW_curves_pos_buffer_get(ob);
if (inst_.is_viewport()) {
data.pos_buf = DRW_curves_pos_buffer_get(ob);
}
else {
data.pos_buf = draw::curves_pos_buffer_get(inst_.scene, ob);
}
break;
case OB_POINTCLOUD:
data.pos_buf = DRW_pointcloud_position_and_radius_buffer_get(ob);
@ -251,7 +273,7 @@ void VelocityModule::geometry_steps_fill()
vel.geo.len[step_] = geom.len;
vel.geo.ofs[step_] = geom.ofs;
/* Avoid reuse. */
vel.id = nullptr;
vel.id = 0;
}
geometry_map.clear();
@ -263,7 +285,6 @@ void VelocityModule::geometry_steps_fill()
*/
void VelocityModule::step_swap()
{
auto swap_steps = [&](eVelocityStep step_a, eVelocityStep step_b) {
std::swap(object_steps[step_a], object_steps[step_b]);
std::swap(geometry_steps[step_a], geometry_steps[step_b]);

View File

@ -29,8 +29,8 @@ namespace blender::eevee {
class VelocityModule {
public:
struct VelocityObjectData : public VelocityIndex {
/** ID to retrieve the corresponding #VelocityGeometryData after copy. */
ID *id;
/** ID key to retrieve the corresponding #VelocityGeometryData after copy. */
uint64_t id;
};
struct VelocityGeometryData {
/** VertBuf not yet ready to be copied to the #VelocityGeometryBuf. */
@ -46,8 +46,8 @@ class VelocityModule {
* geometry offset.
*/
Map<ObjectKey, VelocityObjectData> velocity_map;
/** Geometry to be copied to VelocityGeometryBuf. Indexed by evaluated ID *. Empty after */
Map<ID *, VelocityGeometryData> geometry_map;
/** Geometry to be copied to VelocityGeometryBuf. Indexed by evaluated ID hash. Empty after */
Map<uint64_t, VelocityGeometryData> geometry_map;
/** Contains all objects matrices for each time step. */
std::array<VelocityObjectBuf *, 3> object_steps;
/** Contains all Geometry steps from deforming objects for each time step. */

View File

@ -14,6 +14,19 @@
namespace blender::draw {
/** Hair. */
void hair_init();
GPUVertBuf *hair_pos_buffer_get(Scene *scene,
Object *object,
ParticleSystem *psys,
ModifierData *md);
void hair_update(Manager &manager);
void hair_free();
GPUBatch *hair_sub_pass_setup(PassMain::Sub &sub_ps,
const Scene *scene,
Object *object,
@ -28,6 +41,16 @@ GPUBatch *hair_sub_pass_setup(PassSimple::Sub &sub_ps,
ModifierData *md,
GPUMaterial *gpu_material = nullptr);
/** Curves. */
void curves_init();
GPUVertBuf *curves_pos_buffer_get(Scene *scene, Object *object);
void curves_update(Manager &manager);
void curves_free();
GPUBatch *curves_sub_pass_setup(PassMain::Sub &ps,
const Scene *scene,
Object *ob,
@ -38,6 +61,8 @@ GPUBatch *curves_sub_pass_setup(PassSimple::Sub &ps,
Object *ob,
GPUMaterial *gpu_material = nullptr);
/* Point cloud. */
GPUBatch *point_cloud_sub_pass_setup(PassMain::Sub &sub_ps,
Object *object,
GPUMaterial *gpu_material = nullptr);
@ -46,6 +71,8 @@ GPUBatch *point_cloud_sub_pass_setup(PassSimple::Sub &sub_ps,
Object *object,
GPUMaterial *gpu_material = nullptr);
/** Volume. */
/**
* Add attribute bindings of volume grids to an existing pass.
* No draw call is added so the caller can decide how to use the data.

View File

@ -88,6 +88,25 @@ static GPUShader *curves_eval_shader_get(CurvesEvalShader type)
return DRW_shader_curves_refine_get(type, drw_curves_shader_type_get());
}
static void drw_curves_ensure_dummy_vbo()
{
if (g_dummy_vbo != nullptr) {
return;
}
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
}
void DRW_curves_init(DRWData *drw_data)
{
/* Initialize legacy hair too, to avoid verbosity in callers. */
@ -106,20 +125,7 @@ void DRW_curves_init(DRWData *drw_data)
g_tf_pass = DRW_pass_create("Update Curves Pass", DRW_STATE_WRITE_COLOR);
}
if (g_dummy_vbo == nullptr) {
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
}
drw_curves_ensure_dummy_vbo();
}
void DRW_curves_ubos_pool_free(CurvesUniformBufPool *pool)
@ -553,12 +559,102 @@ void DRW_curves_free()
namespace blender::draw {
static PassSimple *g_pass = nullptr;
void curves_init()
{
if (!g_pass) {
g_pass = MEM_new<PassSimple>("drw_curves g_pass", "Update Curves Pass");
}
g_pass->init();
g_pass->state_set(DRW_STATE_NO_DRAW);
}
static CurvesEvalCache *curves_cache_get(Curves &curves,
GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
CurvesEvalCache *cache;
const bool update = curves_ensure_procedural_data(
&curves, &cache, gpu_material, subdiv, thickness_res);
if (!update) {
return cache;
}
const int strands_len = cache->strands_len;
const int final_points_len = cache->final[subdiv].strands_res * strands_len;
auto cache_update = [&](GPUVertBuf *output_buf, GPUVertBuf *input_buf) {
PassSimple::Sub &ob_ps = g_pass->sub("Object Pass");
ob_ps.shader_set(
DRW_shader_curves_refine_get(CURVES_EVAL_CATMULL_ROM, PART_REFINE_SHADER_COMPUTE));
ob_ps.bind_texture("hairPointBuffer", input_buf);
ob_ps.bind_texture("hairStrandBuffer", cache->proc_strand_buf);
ob_ps.bind_texture("hairStrandSegBuffer", cache->proc_strand_seg_buf);
ob_ps.push_constant("hairStrandsRes", &cache->final[subdiv].strands_res);
ob_ps.bind_ssbo("posTime", output_buf);
const int max_strands_per_call = GPU_max_work_group_count(0);
int strands_start = 0;
while (strands_start < strands_len) {
int batch_strands_len = std::min(strands_len - strands_start, max_strands_per_call);
PassSimple::Sub &sub_ps = ob_ps.sub("Sub Pass");
sub_ps.push_constant("hairStrandOffset", strands_start);
sub_ps.dispatch(int3(batch_strands_len, cache->final[subdiv].strands_res, 1));
strands_start += batch_strands_len;
}
};
if (final_points_len > 0) {
cache_update(cache->final[subdiv].proc_buf, cache->proc_point_buf);
const DRW_Attributes &attrs = cache->final[subdiv].attr_used;
for (int i : IndexRange(attrs.num_requests)) {
/* Only refine point attributes. */
if (attrs.requests[i].domain != ATTR_DOMAIN_CURVE) {
cache_update(cache->final[subdiv].attributes_buf[i], cache->proc_attributes_buf[i]);
}
}
}
return cache;
}
GPUVertBuf *curves_pos_buffer_get(Scene *scene, Object *object)
{
const int subdiv = scene->r.hair_subdiv;
const int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
Curves &curves = *static_cast<Curves *>(object->data);
CurvesEvalCache *cache = curves_cache_get(curves, nullptr, subdiv, thickness_res);
return cache->final[subdiv].proc_buf;
}
void curves_update(Manager &manager)
{
manager.submit(*g_pass);
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
}
void curves_free()
{
MEM_delete(g_pass);
g_pass = nullptr;
}
template<typename PassT>
GPUBatch *curves_sub_pass_setup_implementation(PassT &sub_ps,
const Scene *scene,
Object *ob,
GPUMaterial *gpu_material)
{
/** NOTE: This still relies on the old DRW_curves implementation. */
CurvesUniformBufPool *pool = DST.vmempool->curves_ubos;
CurvesInfosBuf &curves_infos = pool->alloc();
BLI_assert(ob->type == OB_CURVES);

View File

@ -71,6 +71,30 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement)
return DRW_shader_hair_refine_get(refinement, drw_hair_shader_type_get());
}
static void drw_hair_ensure_vbo()
{
if (g_dummy_vbo != nullptr) {
return;
}
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
/* Create VBO immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
g_dummy_curves_info = MEM_new<blender::draw::UniformBuffer<CurvesInfos>>("g_dummy_curves_info");
memset(
g_dummy_curves_info->is_point_attribute, 0, sizeof(g_dummy_curves_info->is_point_attribute));
g_dummy_curves_info->push_update();
}
void DRW_hair_init()
{
if (GPU_transform_feedback_support() || GPU_compute_shader_support()) {
@ -80,27 +104,7 @@ void DRW_hair_init()
g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR);
}
if (g_dummy_vbo == nullptr) {
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
/* Create VBO immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
g_dummy_curves_info = MEM_new<blender::draw::UniformBuffer<CurvesInfos>>(
"g_dummy_curves_info");
memset(g_dummy_curves_info->is_point_attribute,
0,
sizeof(g_dummy_curves_info->is_point_attribute));
g_dummy_curves_info->push_update();
}
drw_hair_ensure_vbo();
}
static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp,
@ -437,6 +441,86 @@ void DRW_hair_free()
namespace blender::draw {
static PassSimple *g_pass = nullptr;
void hair_init()
{
if (!g_pass) {
g_pass = MEM_new<PassSimple>("drw_hair g_pass", "Update Hair Pass");
}
g_pass->init();
g_pass->state_set(DRW_STATE_NO_DRAW);
}
static ParticleHairCache *hair_particle_cache_get(Object *object,
ParticleSystem *psys,
ModifierData *md,
GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
ParticleHairCache *cache;
bool update = particles_ensure_procedural_data(
object, psys, md, &cache, gpu_material, subdiv, thickness_res);
if (!update) {
return cache;
}
const int strands_len = cache->strands_len;
const int final_points_len = cache->final[subdiv].strands_res * strands_len;
if (final_points_len > 0) {
PassSimple::Sub &ob_ps = g_pass->sub("Object Pass");
ob_ps.shader_set(
DRW_shader_hair_refine_get(PART_REFINE_CATMULL_ROM, PART_REFINE_SHADER_COMPUTE));
ob_ps.bind_texture("hairPointBuffer", cache->proc_point_buf);
ob_ps.bind_texture("hairStrandBuffer", cache->proc_strand_buf);
ob_ps.bind_texture("hairStrandSegBuffer", cache->proc_strand_seg_buf);
ob_ps.push_constant("hairStrandsRes", &cache->final[subdiv].strands_res);
ob_ps.bind_ssbo("posTime", cache->final[subdiv].proc_buf);
const int max_strands_per_call = GPU_max_work_group_count(0);
int strands_start = 0;
while (strands_start < strands_len) {
int batch_strands_len = std::min(strands_len - strands_start, max_strands_per_call);
PassSimple::Sub &sub_ps = ob_ps.sub("Sub Pass");
sub_ps.push_constant("hairStrandOffset", strands_start);
sub_ps.dispatch(int3(batch_strands_len, cache->final[subdiv].strands_res, 1));
strands_start += batch_strands_len;
}
}
return cache;
}
GPUVertBuf *hair_pos_buffer_get(Scene *scene,
Object *object,
ParticleSystem *psys,
ModifierData *md)
{
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *cache = hair_particle_cache_get(
object, psys, md, nullptr, subdiv, thickness_res);
return cache->final[subdiv].proc_buf;
}
void hair_update(Manager &manager)
{
manager.submit(*g_pass);
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
}
void hair_free()
{
MEM_delete(g_pass);
g_pass = nullptr;
}
template<typename PassT>
GPUBatch *hair_sub_pass_setup_implementation(PassT &sub_ps,
const Scene *scene,
@ -445,6 +529,8 @@ GPUBatch *hair_sub_pass_setup_implementation(PassT &sub_ps,
ModifierData *md,
GPUMaterial *gpu_material)
{
/** NOTE: This still relies on the old DRW_hair implementation. */
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *hair_cache = drw_hair_particle_cache_get(