From f8637cd8af451661a306edd5682cc17029e7e7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 02:30:50 +0200 Subject: Alembic Procedural: only subdivide if subsurf modifier is present As subdivision objects are first class citizens in Alembic, to differentiate them with non-subdivided polygon meshes, the Alembic Procedural automatically sets up subdivision properties on the generated Cycles Mesh. However, for real-time playback subdivision is far too slow, so this modifies the detection of a MeshSeqCache modifier used to activate the procedural to allow for a Subsurf modifier right after the cache one. If present, the procedural will tag the object for subdivision, if absent, the object will be treated as a regular mesh. This is a temporary measure for until subdivision surface settings are part of the Mesh datablock (see T68891). Reviewed By: brecht Differential Revision: https://developer.blender.org/D11162 --- intern/cycles/blender/blender_mesh.cpp | 4 +-- intern/cycles/blender/blender_object.cpp | 11 ++++-- intern/cycles/blender/blender_sync.h | 4 ++- intern/cycles/blender/blender_util.h | 12 ++++++- intern/cycles/render/alembic.cpp | 57 +++++++++++++++++++++++++++++--- intern/cycles/render/alembic.h | 9 +++++ 6 files changed, 85 insertions(+), 12 deletions(-) (limited to 'intern') diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index d1042277183..ebba6981502 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -1078,7 +1078,7 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *me return; } - BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, true); + BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, true, nullptr); if (!b_mesh_cache) { return; @@ -1241,7 +1241,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, } /* Cached motion blur already exported. */ - BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, true); + BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, true, nullptr); if (mesh_cache) { return; } diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 657ecdeeeb7..2dbebac4cc3 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -485,7 +485,9 @@ bool BlenderSync::sync_object_attributes(BL::DepsgraphObjectInstance &b_instance /* Object Loop */ -void BlenderSync::sync_procedural(BL::Object &b_ob, BL::MeshSequenceCacheModifier &b_mesh_cache) +void BlenderSync::sync_procedural(BL::Object &b_ob, + BL::MeshSequenceCacheModifier &b_mesh_cache, + bool has_subdivision_modifier) { #ifdef WITH_ALEMBIC BL::CacheFile cache_file = b_mesh_cache.cache_file(); @@ -534,6 +536,8 @@ void BlenderSync::sync_procedural(BL::Object &b_ob, BL::MeshSequenceCacheModifie abc_object->set_subd_dicing_rate(subd_dicing_rate); abc_object->set_subd_max_level(max_subdivisions); + abc_object->set_ignore_subdivision(!has_subdivision_modifier); + if (abc_object->is_modified() || procedural->is_modified()) { procedural->tag_update(scene); } @@ -601,13 +605,14 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, if (b_instance.show_self()) { #ifdef WITH_ALEMBIC bool use_procedural = false; + bool has_subdivision_modifier = false; BL::MeshSequenceCacheModifier b_mesh_cache(PointerRNA_NULL); /* Experimental as Blender does not have good support for procedurals at the moment, also * only available in preview renders since currently do not have a good cache policy, the * data being loaded at once for all the frames. */ if (experimental && b_v3d) { - b_mesh_cache = object_mesh_cache_find(b_ob, false); + b_mesh_cache = object_mesh_cache_find(b_ob, false, &has_subdivision_modifier); use_procedural = b_mesh_cache && b_mesh_cache.cache_file().use_render_procedural(); } @@ -615,7 +620,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, /* Skip in the motion case, as generating motion blur data will be handled in the * procedural. */ if (!motion) { - sync_procedural(b_ob, b_mesh_cache); + sync_procedural(b_ob, b_mesh_cache, has_subdivision_modifier); } } else diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 0e605fcbf16..44322dda6b9 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -151,7 +151,9 @@ class BlenderSync { TaskPool *geom_task_pool); void sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob, Object *object); - void sync_procedural(BL::Object &b_ob, BL::MeshSequenceCacheModifier &b_mesh_cache); + void sync_procedural(BL::Object &b_ob, + BL::MeshSequenceCacheModifier &b_mesh_cache, + bool has_subdivision); bool sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object); diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index e50b1a4760e..3cf75b338dc 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -573,7 +573,8 @@ static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b } static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob, - bool check_velocity) + bool check_velocity, + bool *has_subdivision_modifier) { for (int i = b_ob.modifiers.length() - 1; i >= 0; --i) { BL::Modifier b_mod = b_ob.modifiers[i]; @@ -595,6 +596,15 @@ static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b continue; } + /* Only skip the subsurf modifier if we are not checking for the mesh sequence cache modifier + * for motion blur. */ + if (b_mod.type() == BL::Modifier::type_SUBSURF && !check_velocity) { + if (has_subdivision_modifier) { + *has_subdivision_modifier = true; + } + continue; + } + break; } diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index c1817016955..81f47256739 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -385,6 +385,8 @@ NODE_DEFINE(AlembicObject) SOCKET_STRING(path, "Alembic Path", ustring()); SOCKET_NODE_ARRAY(used_shaders, "Used Shaders", Shader::get_node_type()); + SOCKET_BOOLEAN(ignore_subdivision, "Ignore Subdivision", true); + SOCKET_INT(subd_max_level, "Max Subdivision Level", 1); SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 1.0f); @@ -470,6 +472,33 @@ void AlembicObject::load_data_in_cache(CachedData &cached_data, cached_data.clear(); + if (this->get_ignore_subdivision()) { + PolyMeshSchemaData data; + data.topology_variance = schema.getTopologyVariance(); + data.time_sampling = schema.getTimeSampling(); + data.positions = schema.getPositionsProperty(); + data.face_counts = schema.getFaceCountsProperty(); + data.face_indices = schema.getFaceIndicesProperty(); + data.num_samples = schema.getNumSamples(); + data.velocities = schema.getVelocitiesProperty(); + data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders()); + + read_geometry_data(proc, cached_data, data, progress); + + if (progress.get_cancel()) { + return; + } + + /* Use the schema as the base compound property to also be able to look for top level + * properties. */ + read_attributes( + proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress); + + cached_data.invalidate_last_loaded_time(true); + data_loaded = true; + return; + } + SubDSchemaData data; data.time_sampling = schema.getTimeSampling(); data.num_samples = schema.getNumSamples(); @@ -781,6 +810,19 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate); + /* Clear the subdivision caches as the data is stored differently. */ + for (Node *node : objects) { + AlembicObject *object = static_cast(node); + + if (object->schema_type != AlembicObject::SUBD) { + continue; + } + + if (object->ignore_subdivision_is_modified()) { + object->clear_cache(); + } + } + build_caches(progress); foreach (Node *node, objects) { @@ -967,13 +1009,13 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame_time) { - CachedData &cached_data = abc_object->get_cached_data(); - - if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) { - /* need to reset the current data is something changed */ - cached_data.invalidate_last_loaded_time(); + if (abc_object->get_ignore_subdivision()) { + read_mesh(abc_object, frame_time); + return; } + CachedData &cached_data = abc_object->get_cached_data(); + /* Update sockets. */ Object *object = abc_object->get_object(); @@ -988,6 +1030,11 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame return; } + if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) { + /* need to reset the current data is something changed */ + cached_data.invalidate_last_loaded_time(); + } + Mesh *mesh = static_cast(object->get_geometry()); /* Make sure shader ids are also updated. */ diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h index 61c0e40fe4a..9c58af720f6 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/render/alembic.h @@ -353,6 +353,10 @@ class AlembicObject : public Node { /* Shaders used for rendering. */ NODE_SOCKET_API_ARRAY(array, used_shaders) + /* Treat this subdivision object as a regular polygon mesh, so no subdivision will be performed. + */ + NODE_SOCKET_API(bool, ignore_subdivision) + /* Maximum number of subdivisions for ISubD objects. */ NODE_SOCKET_API(int, subd_max_level) @@ -416,6 +420,11 @@ class AlembicObject : public Node { return cached_data_.is_constant(); } + void clear_cache() + { + cached_data_.clear(); + } + Object *object = nullptr; bool data_loaded = false; -- cgit v1.2.3 From accf3045be40433045c197cfcdbcbc32a7724403 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 20 Aug 2021 09:05:52 -0300 Subject: Fix memory leak while processing mouse event Assignment missed. --- intern/ghost/intern/GHOST_SystemWin32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'intern') diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 347067eae50..f44107ee000 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1741,7 +1741,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_MOUSELEAVE: { window->m_mousePresent = false; if (window->getTabletData().Active == GHOST_kTabletModeNone) { - processCursorEvent(window); + event = processCursorEvent(window); } GHOST_Wintab *wt = window->getWintab(); if (wt) { -- cgit v1.2.3 From 9bfc47c9334bc4fbecbe7871fff9af4cc46c8832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Fri, 20 Aug 2021 14:29:05 +0200 Subject: Alembic Procedural: basic cache control settings This adds a setting to enable data caching, and another one to set the maximum cache size in megabytes. When caching is enabled we load the data for the entire animation in memory, as we already do, however, if the data exceeds the memory limit, render is aborted. When caching is disabled, we simply load the data for the current frame in memory. Ref D10197 Reviewed By: brecht Differential Revision: https://developer.blender.org/D11163 --- intern/cycles/blender/blender_object.cpp | 3 ++ intern/cycles/render/alembic.cpp | 70 ++++++++++++++++++++++++++++++++ intern/cycles/render/alembic.h | 30 ++++++++++++++ intern/cycles/render/alembic_read.cpp | 16 ++++++-- 4 files changed, 116 insertions(+), 3 deletions(-) (limited to 'intern') diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 2dbebac4cc3..a7eae421b55 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -523,6 +523,9 @@ void BlenderSync::sync_procedural(BL::Object &b_ob, procedural->set_scale(cache_file.scale()); + procedural->set_use_prefetch(cache_file.use_prefetch()); + procedural->set_prefetch_cache_size(cache_file.prefetch_cache_size()); + /* create or update existing AlembicObjects */ ustring object_path = ustring(b_mesh_cache.object_path()); diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index 81f47256739..69bc0712674 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -25,6 +25,7 @@ #include "render/shader.h" #include "util/util_foreach.h" +#include "util/util_logging.h" #include "util/util_progress.h" #include "util/util_transform.h" #include "util/util_vector.h" @@ -211,6 +212,35 @@ void CachedData::set_time_sampling(TimeSampling time_sampling) } } +size_t CachedData::memory_used() const +{ + size_t mem_used = 0; + + mem_used += curve_first_key.memory_used(); + mem_used += curve_keys.memory_used(); + mem_used += curve_radius.memory_used(); + mem_used += curve_shader.memory_used(); + mem_used += num_ngons.memory_used(); + mem_used += shader.memory_used(); + mem_used += subd_creases_edge.memory_used(); + mem_used += subd_creases_weight.memory_used(); + mem_used += subd_face_corners.memory_used(); + mem_used += subd_num_corners.memory_used(); + mem_used += subd_ptex_offset.memory_used(); + mem_used += subd_smooth.memory_used(); + mem_used += subd_start_corner.memory_used(); + mem_used += transforms.memory_used(); + mem_used += triangles.memory_used(); + mem_used += uv_loops.memory_used(); + mem_used += vertices.memory_used(); + + for (const CachedAttribute &attr : attributes) { + mem_used += attr.data.memory_used(); + } + + return mem_used; +} + static M44d convert_yup_zup(const M44d &mtx, float scale_mult) { V3d scale, shear, rotation, translation; @@ -706,6 +736,9 @@ NODE_DEFINE(AlembicProcedural) SOCKET_NODE_ARRAY(objects, "Objects", AlembicObject::get_node_type()); + SOCKET_BOOLEAN(use_prefetch, "Use Prefetch", true); + SOCKET_INT(prefetch_cache_size, "Prefetch Cache Size", 4096); + return type; } @@ -823,6 +856,30 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) } } + if (use_prefetch_is_modified()) { + if (!use_prefetch) { + for (Node *node : objects) { + AlembicObject *object = static_cast(node); + object->clear_cache(); + } + } + } + + if (prefetch_cache_size_is_modified()) { + /* Check whether the current memory usage fits in the new requested size, + * abort the render if it is any higher. */ + size_t memory_used = 0ul; + for (Node *node : objects) { + AlembicObject *object = static_cast(node); + memory_used += object->get_cached_data().memory_used(); + } + + if (memory_used > get_prefetch_cache_size_in_bytes()) { + progress.set_error("Error: Alembic Procedural memory limit reached"); + return; + } + } + build_caches(progress); foreach (Node *node, objects) { @@ -1300,6 +1357,8 @@ void AlembicProcedural::walk_hierarchy( void AlembicProcedural::build_caches(Progress &progress) { + size_t memory_used = 0; + for (Node *node : objects) { AlembicObject *object = static_cast(node); @@ -1353,7 +1412,18 @@ void AlembicProcedural::build_caches(Progress &progress) if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) { object->setup_transform_cache(object->get_cached_data(), scale); } + + memory_used += object->get_cached_data().memory_used(); + + if (use_prefetch) { + if (memory_used > get_prefetch_cache_size_in_bytes()) { + progress.set_error("Error: Alembic Procedural memory limit reached"); + return; + } + } } + + VLOG(1) << "AlembicProcedural memory usage : " << string_human_readable_size(memory_used); } CCL_NAMESPACE_END diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h index 9c58af720f6..8e166a5ab04 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/render/alembic.h @@ -272,6 +272,21 @@ template class DataStore { node->set(*socket, value); } + size_t memory_used() const + { + if constexpr (is_array::value) { + size_t mem_used = 0; + + for (const T &array : data) { + mem_used += array.size() * sizeof(array[0]); + } + + return mem_used; + } + + return data.size() * sizeof(T); + } + private: const TimeIndexPair &get_index_for_time(double time) const { @@ -332,6 +347,8 @@ struct CachedData { void invalidate_last_loaded_time(bool attributes_only = false); void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling); + + size_t memory_used() const; }; /* Representation of an Alembic object for the AlembicProcedural. @@ -482,6 +499,13 @@ class AlembicProcedural : public Procedural { * software. */ NODE_SOCKET_API(float, scale) + /* Cache controls */ + NODE_SOCKET_API(bool, use_prefetch) + + /* Memory limit for the cache, if the data does not fit within this limit, rendering is aborted. + */ + NODE_SOCKET_API(int, prefetch_cache_size) + AlembicProcedural(); ~AlembicProcedural(); @@ -531,6 +555,12 @@ class AlembicProcedural : public Procedural { void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); void build_caches(Progress &progress); + + size_t get_prefetch_cache_size_in_bytes() const + { + /* prefetch_cache_size is in megabytes, so convert to bytes. */ + return static_cast(prefetch_cache_size) * 1024 * 1024; + } }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp index c53ec668938..b105af63b44 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/render/alembic_read.cpp @@ -44,9 +44,19 @@ static set get_relevant_sample_times(AlembicProcedural *proc, return result; } - // load the data for the entire animation - const double start_frame = static_cast(proc->get_start_frame()); - const double end_frame = static_cast(proc->get_end_frame()); + double start_frame; + double end_frame; + + if (proc->get_use_prefetch()) { + // load the data for the entire animation + start_frame = static_cast(proc->get_start_frame()); + end_frame = static_cast(proc->get_end_frame()); + } + else { + // load the data for the current frame + start_frame = static_cast(proc->get_frame()); + end_frame = start_frame; + } const double frame_rate = static_cast(proc->get_frame_rate()); const double start_time = start_frame / frame_rate; -- cgit v1.2.3 From cd8d9383e74a798e75ebced95cdd6d8de9f02369 Mon Sep 17 00:00:00 2001 From: Alaska Date: Fri, 20 Aug 2021 17:43:15 +0200 Subject: Fix T90804: small grammatical error in noise threshold description Differential Revision: https://developer.blender.org/D12277 --- intern/cycles/blender/addon/properties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'intern') diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index ac0aca57028..0c3af3fabeb 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -408,7 +408,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): adaptive_threshold: FloatProperty( name="Adaptive Sampling Threshold", - description="Noise level step to stop sampling at, lower values reduce noise the cost of render time. Zero for automatic setting based on number of AA samples", + description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples", min=0.0, max=1.0, default=0.0, precision=4, -- cgit v1.2.3 From c671bfe14e8d8c72a146b74e4dcc1c8ee7ab5bb4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Aug 2021 13:23:39 +1000 Subject: Cleanup: spelling in comments & minor cleanup Also hyphenate 'mouse-move' use doxy sections in render_update.c & move function comment from the header to the source. --- intern/ghost/GHOST_IEvent.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'intern') diff --git a/intern/ghost/GHOST_IEvent.h b/intern/ghost/GHOST_IEvent.h index bcccd536ebd..239eea21088 100644 --- a/intern/ghost/GHOST_IEvent.h +++ b/intern/ghost/GHOST_IEvent.h @@ -32,10 +32,10 @@ class GHOST_IWindow; /** * Interface class for events received from GHOST. * You should not need to inherit this class. The system will pass these events - * to the GHOST_IEventConsumer::processEvent() method of event consumers.
- * Use the getType() method to retrieve the type of event and the getData() + * to the #GHOST_IEventConsumer::processEvent() method of event consumers.
+ * Use the #getType() method to retrieve the type of event and the #getData() * method to get the event data out. Using the event type you can cast the - * event data to the correct event dat structure. + * event data to the correct event data structure. * \see GHOST_IEventConsumer#processEvent * \see GHOST_TEventType */ -- cgit v1.2.3