diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 0d7799759d5..12784150549 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -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(); diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc index 84eef9f469b..12dba2e7e85 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.cc +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc @@ -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]); diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh index a35454a01a9..27dca23cc5a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.hh +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh @@ -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 velocity_map; - /** Geometry to be copied to VelocityGeometryBuf. Indexed by evaluated ID *. Empty after */ - Map geometry_map; + /** Geometry to be copied to VelocityGeometryBuf. Indexed by evaluated ID hash. Empty after */ + Map geometry_map; /** Contains all objects matrices for each time step. */ std::array object_steps; /** Contains all Geometry steps from deforming objects for each time step. */ diff --git a/source/blender/draw/intern/draw_common.hh b/source/blender/draw/intern/draw_common.hh index cfc1b38eecd..8f7d0bfba84 100644 --- a/source/blender/draw/intern/draw_common.hh +++ b/source/blender/draw/intern/draw_common.hh @@ -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. diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index 3beb75cea04..042979ad511 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -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("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(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 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); diff --git a/source/blender/draw/intern/draw_hair.cc b/source/blender/draw/intern/draw_hair.cc index eae67d7d490..06bda743ab9 100644 --- a/source/blender/draw/intern/draw_hair.cc +++ b/source/blender/draw/intern/draw_hair.cc @@ -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>("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>( - "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("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 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(