Geometry Nodes: support materials in simulation state with data-block mapping #109703

Closed
Jacques Lucke wants to merge 80 commits from JacquesLucke/blender:geometry-nodes-id-mapping into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
Member

This implements the design proposed in #108410 to support materials in the simulations.
The task also describes why this is not a trivial issue.

The solution is to have an explicit name (+ library name) -> data block mapping that is stored in the modifier. The library name is necessary for it to be a unique identifier within a .blend file. The baked data only contains names of the used materials. When the baked data is loaded, the correct material is looked up from the mapping.

The most tricky aspect here is to decide when the mapping is created. There are different possibilities like forcing the user to create it manually, updating it automatically after specific user actions, or creating it automatically during depsgraph evaluation whenever a missing mapping is detected.

Currently, the last option is used even though it is quite unconventional, because it leads to the best out-of-the-box user experience. The main difficulties are:

  • Adding the mapping changes the depsgraph, but that should generally not be done while the depsgraph is evaluating. In this specific case, it's not as bad, because we know that the relation added by the mapping exists, at least indirectly, already. I did run into a few asserts while working on this, that checked validity of evaluated data. However, I may have triggered those by bugs that are fixed already.
  • Adding the mapping increases the user count of the mapped data block. Since multiple modifiers might create the mapping from different threads, the user count would have to be increased in a thread-safe way. I'm not aware of any other place where this is the case yet.

Possible further developments:

  • Show some warning/operator in the simulation zone to make the workflow of creating the mapping easier.
  • Use the same mapping for other data block types. E.g. the baked data could contain instances that reference an object in the .blend file, but we don't want to store the geometry of that object inside of the cache.
  • Currently, there is one mapping per modifier. In theory, the mapping could also exist at higher (e.g. per object) and at lower levels (e.g. per simulation zone). Having it in the modifier, shouldn't prevent us from having the mapping in other places in the future.
  • To ease reusing cached baked data in other .blend files, it could be useful to store the mapping on a separate data block. This way, the other .blend file would just have to link/append the data block that contains the mapping to get all the dependencies automatically.

Possible TODOs for this patch:

  • Move the data block mappings panel to a different subpanel.
  • Add a button to clear all mappings. Otherwise it might be annoying to get rid of potentially very many mappings.
  • Defer adding relations to the original modifier. The goal with this would be make the difficulties mentioned above less bad. There seem to be two main options:
    • Have a special depsgraph node that writes back new data to the original data. This way, we don't have to synchronise between different threads writing back to original data.
    • Use post-processing step after depsgraph evaluation that modifies the original data. This has the benefit that original data is only modified from the main thread and the synchronisation becomes unnecessary. Furthermore, it would at least give us the option to update depsgraph relations immediatly, avoiding the out-of-sync state (which some asserts might not like). Also, it would give us the option to add an undo step if we wanted to.

image

This implements the design proposed in #108410 to support materials in the simulations. The task also describes why this is not a trivial issue. The solution is to have an explicit `name (+ library name) -> data block` mapping that is stored in the modifier. The `library name` is necessary for it to be a unique identifier within a .blend file. The baked data only contains names of the used materials. When the baked data is loaded, the correct material is looked up from the mapping. The most tricky aspect here is to decide when the mapping is created. There are different possibilities like forcing the user to create it manually, updating it automatically after specific user actions, or creating it automatically during depsgraph evaluation whenever a missing mapping is detected. Currently, the last option is used even though it is quite unconventional, because it leads to the best out-of-the-box user experience. The main difficulties are: * Adding the mapping changes the depsgraph, but that should generally not be done while the depsgraph is evaluating. In this specific case, it's not as bad, because we know that the relation added by the mapping exists, at least indirectly, already. I did run into a few asserts while working on this, that checked validity of evaluated data. However, I may have triggered those by bugs that are fixed already. * Adding the mapping increases the user count of the mapped data block. Since multiple modifiers might create the mapping from different threads, the user count would have to be increased in a thread-safe way. I'm not aware of any other place where this is the case yet. Possible further developments: * Show some warning/operator in the simulation zone to make the workflow of creating the mapping easier. * Use the same mapping for other data block types. E.g. the baked data could contain instances that reference an object in the .blend file, but we don't want to store the geometry of that object inside of the cache. * Currently, there is one mapping per modifier. In theory, the mapping could also exist at higher (e.g. per object) and at lower levels (e.g. per simulation zone). Having it in the modifier, shouldn't prevent us from having the mapping in other places in the future. * To ease reusing cached baked data in other .blend files, it could be useful to store the mapping on a separate data block. This way, the other .blend file would just have to link/append the data block that contains the mapping to get all the dependencies automatically. Possible TODOs for this patch: * Move the data block mappings panel to a different subpanel. * Add a button to clear all mappings. Otherwise it might be annoying to get rid of potentially very many mappings. * Defer adding relations to the original modifier. The goal with this would be make the difficulties mentioned above less bad. There seem to be two main options: * Have a special depsgraph node that writes back new data to the original data. This way, we don't have to synchronise between different threads writing back to original data. * Use post-processing step after depsgraph evaluation that modifies the original data. This has the benefit that original data is only modified from the main thread and the synchronisation becomes unnecessary. Furthermore, it would at least give us the option to update depsgraph relations immediatly, avoiding the out-of-sync state (which some asserts might not like). Also, it would give us the option to add an undo step if we wanted to. ![image](/attachments/67f940b4-ea40-4ed4-983d-4e06aeadd3bd)
Jacques Lucke added 20 commits 2023-07-04 17:14:27 +02:00
Hans Goudey reviewed 2023-07-04 19:30:05 +02:00
@ -1381,2 +1467,2 @@
"internal_dependencies",
N_("Internal Dependencies"),
"id_mappings",
N_("ID Mappings"),
Member

In the UI, these should be referred to as "Data Blocks" rather than "IDs". ID is just the name of the struct in the code, and it's not even a great name :P

In the UI, these should be referred to as "Data Blocks" rather than "IDs". `ID` is just the name of the struct in the code, and it's not even a great name :P
JacquesLucke marked this conversation as resolved
Hans Goudey added this to the Nodes & Physics project 2023-07-10 14:04:46 +02:00
Hans Goudey added the
Interest
Geometry Nodes
label 2023-07-10 14:05:00 +02:00
Jacques Lucke added 2 commits 2023-07-11 13:03:13 +02:00
Jacques Lucke added 4 commits 2023-07-11 14:17:58 +02:00
Jacques Lucke added 2 commits 2023-07-11 14:39:22 +02:00
Jacques Lucke added 5 commits 2023-07-12 11:27:29 +02:00
Jacques Lucke added 2 commits 2023-07-12 13:37:46 +02:00
Jacques Lucke changed title from WIP: Geometry Nodes: support materials in simulation state with id mapping to Geometry Nodes: support materials in simulation state with data-block mapping 2023-07-12 14:02:29 +02:00
Jacques Lucke requested review from Simon Thommes 2023-07-12 14:02:47 +02:00
Jacques Lucke requested review from Hans Goudey 2023-07-12 14:02:47 +02:00
Hans Goudey requested changes 2023-07-12 15:00:34 +02:00
Hans Goudey left a comment
Member

In general this makes sense I think, noticed some things to change inline though.

Could the bake operator create the mapping maybe?

In general this makes sense I think, noticed some things to change inline though. Could the bake operator create the mapping maybe?
@ -40,9 +40,21 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
layout.template_grease_pencil_modifiers()
class DATA_UL_nodes_modifier_id_mappings(UIList):
Member

Better to define this in the modifier file like MOD_gpencil_legacy_dash.c.

Better to define this in the modifier file like `MOD_gpencil_legacy_dash.c`.
JacquesLucke marked this conversation as resolved
@ -377,0 +385,4 @@
IDProperty *materials_prop = IDP_NewIDPArray(".materials");
for (const int i : io_materials->elements().index_range()) {
IDPropertyTemplate idprop = {0};
IDProperty *material_prop = IDP_New(IDP_GROUP, &idprop, std::to_string(i).c_str());
Member

A bit nicer IMO: bke::idprop::create_group(std::to_string(i)).release()

A bit nicer IMO: `bke::idprop::create_group(std::to_string(i)).release()`
JacquesLucke marked this conversation as resolved
@ -377,0 +388,4 @@
IDProperty *material_prop = IDP_New(IDP_GROUP, &idprop, std::to_string(i).c_str());
const DictionaryValue *io_material = io_materials->elements()[i]->as_dictionary_value();
if (io_material != nullptr) {
Member

How about this?

    if (const DictionaryValue *io_material = io_materials->elements()[i]->as_dictionary_value()) {
How about this? ``` if (const DictionaryValue *io_material = io_materials->elements()[i]->as_dictionary_value()) { ```
JacquesLucke marked this conversation as resolved
@ -377,0 +398,4 @@
}
IDP_AppendArray(materials_prop, material_prop);
/* IDP_AppendArray does a shallo copy. */
Member

shallo -> shallow

`shallo` -> `shallow`
JacquesLucke marked this conversation as resolved
@ -3719,1 +3723,4 @@
/** \} */
/* ------------------------------------------------------------------- */
/** \name Update id mapping in geometry nodes modifier
Member

Section titles are title case

Section titles are title case
JacquesLucke marked this conversation as resolved
@ -3720,0 +3726,4 @@
/** \name Update id mapping in geometry nodes modifier
* \{ */
static ID *find_id_by_names(Main &bmain,
Member

Maybe this should be moved elsewhere? Seems like it might be useful elsewhere, and that would help clarify there's nothing modifier specific here

Maybe this should be moved elsewhere? Seems like it might be useful elsewhere, and that would help clarify there's nothing modifier specific here
JacquesLucke marked this conversation as resolved
@ -3720,0 +3727,4 @@
* \{ */
static ID *find_id_by_names(Main &bmain,
const blender::StringRef &id_name,
Member

Pass StringRef by value, right?

Pass `StringRef` by value, right?
JacquesLucke marked this conversation as resolved
@ -3720,0 +3775,4 @@
nmd.id_mappings = static_cast<NodesModifierIDMapping *>(
MEM_reallocN(nmd.id_mappings, sizeof(NodesModifierIDMapping) * new_mappings_num));
NodesModifierIDMapping *new_mappings = nmd.id_mappings + nmd.id_mappings_num;
Member

int new_mapping_i = nmd.id_mappings_num;

That's a bit nicer than incrementing a raw pointer IMO

` int new_mapping_i = nmd.id_mappings_num;` That's a bit nicer than incrementing a raw pointer IMO
JacquesLucke marked this conversation as resolved
@ -3720,0 +3831,4 @@
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = BKE_object_active_modifier(ob);
Member

These operators should probably use the "modifier" context property rather than the active modifier, so they work properly when interacting with two geometry nodes modifiers.

See the Generic Invoke Functions section in this file for some utilities that help with that.

These operators should probably use the `"modifier"` context property rather than the active modifier, so they work properly when interacting with two geometry nodes modifiers. See the `Generic Invoke Functions` section in this file for some utilities that help with that.
JacquesLucke marked this conversation as resolved
@ -3720,0 +3840,4 @@
if (nmd.active_id_mapping < 0 || nmd.active_id_mapping >= nmd.id_mappings_num) {
return OPERATOR_CANCELLED;
}
NodesModifierIDMapping &active_mapping = nmd.id_mappings[nmd.active_id_mapping];
Member

I wonder if some of this boilerplate could be removed using something like std::remove if the NodesModifierIDMapping had at least some RAII semantics. That could probably help elsewhere too, and maybe it's a good habit to get into.

I wonder if some of this boilerplate could be removed using something like `std::remove` if the `NodesModifierIDMapping` had at least some RAII semantics. That could probably help elsewhere too, and maybe it's a good habit to get into.
Author
Member

I think using RAII for raw DNA structs is more trouble than it's worth currently. Mainly because we have a fair amount of code that deals with DNA structs as raw memory. It would be nice to use RAII for DNA types (or in wrapper types) in the future, but I think we should have a more global design before starting with that.

I think using RAII for raw DNA structs is more trouble than it's worth currently. Mainly because we have a fair amount of code that deals with DNA structs as raw memory. It would be nice to use RAII for DNA types (or in wrapper types) in the future, but I think we should have a more global design before starting with that.
JacquesLucke marked this conversation as resolved
@ -3720,0 +3846,4 @@
id_us_min(active_mapping.id);
active_mapping.id = nullptr;
memmove(nmd.id_mappings + nmd.active_id_mapping,
Member

I notice this doesn't reallocate the array. Maybe imaginary problem, but I'm thinking of a case where there were 1000 mappings and all are removed, and the memory is still used.

I notice this doesn't reallocate the array. Maybe imaginary problem, but I'm thinking of a case where there were 1000 mappings and all are removed, and the memory is still used.
JacquesLucke marked this conversation as resolved
@ -7057,0 +7095,4 @@
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "ID Name", "Name of the ID that is to be mapped");
Member

ID -> Data-Block

Same below

ID -> Data-Block Same below
JacquesLucke marked this conversation as resolved
@ -7057,0 +7100,4 @@
prop = RNA_def_property(srna, "lib_name", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(
prop, "Library Name", "Name of the library that contains the ID that is to be mapped");
Member

ID that is to be mapped -> mapped data-block

Gets the same thing across less awkwardly I think

`ID that is to be mapped` -> `mapped data-block` Gets the same thing across less awkwardly I think
JacquesLucke marked this conversation as resolved
@ -355,2 +355,4 @@
},
&settings);
for (const int i : IndexRange(nmd->id_mappings_num)) {
Member
  for (NodesModifierIDMapping *mapping : MutableSpan(nmd->id_mappings, nmd->id_mappings_num)) {
    walk(userData, ob, &mapping->id, IDWALK_CB_USER);
  }

Same in other places, where possible IMO

``` for (NodesModifierIDMapping *mapping : MutableSpan(nmd->id_mappings, nmd->id_mappings_num)) { walk(userData, ob, &mapping->id, IDWALK_CB_USER); } ``` Same in other places, where possible IMO
JacquesLucke marked this conversation as resolved
@ -144,6 +147,35 @@ static void remove_materials(Material ***materials, short *materials_num)
*materials_num = 0;
}
static void store_materials_as_id_properties(ID &id)
Member

Not really a new problem, but I wonder if it's worth adding some error reporting here. Only issue is that the geo nodes exec params would have to be passed this deep, which is pretty ugly. So maybe not...

Not really a new problem, but I wonder if it's worth adding some error reporting here. Only issue is that the geo nodes exec params would have to be passed this deep, which is pretty ugly. So maybe not...
Author
Member

I listed that in the "Possible further developments" section in the PR description. Note that one does not have to pass GeoNodeExecParams here to be able to create a warning. The logger can be retrieved from GeoNodesLFLocalUserData.

I listed that in the "Possible further developments" section in the PR description. Note that one does not have to pass `GeoNodeExecParams` here to be able to create a warning. The logger can be retrieved from `GeoNodesLFLocalUserData`.
Member

Ah, sorry I missed that

Ah, sorry I missed that
JacquesLucke marked this conversation as resolved
@ -179,0 +264,4 @@
bke::BakeIDMappingIssuesLog *id_mapping_issues)
{
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
Member

Not sure if it's worth checking if there are materials to restore before retrieving write access, I guess if that is actually a performance problem we aren't using implicit sharing enough ;)

Not sure if it's worth checking if there are materials to restore before retrieving write access, I guess if that is actually a performance problem we aren't using implicit sharing enough ;)
Author
Member

Yeah, wouldn't bother with that for now. The mesh needs to be retrieved for writing for renaming anonymous attributes as well.

Yeah, wouldn't bother with that for now. The mesh needs to be retrieved for writing for renaming anonymous attributes as well.
Member

Only when there are anonymous attributes that need to be renamed now. But yes, right :)

Only when there are anonymous attributes that need to be renamed now. But yes, right :)
JacquesLucke marked this conversation as resolved
Jacques Lucke added 6 commits 2023-07-13 11:12:21 +02:00
Jacques Lucke added 6 commits 2023-07-13 17:52:20 +02:00
Hans Goudey requested changes 2023-07-13 19:42:16 +02:00
@ -176,0 +173,4 @@
/**
* All simulation states, sorted by frame.
*/
Vector<std::unique_ptr<ModifierSimulationStateAtFrame>> states_at_frames_;
Member

This looks like a merge error

This looks like a merge error
JacquesLucke marked this conversation as resolved
@ -580,3 +619,2 @@
static std::shared_ptr<io::serialize::ArrayValue> serialize_material_slots(
const Span<const Material *> material_slots)
static void serialize_material_slots(DictionaryValue &io_dict, const IDProperty *id_properties)
Member

What do you think about generalizing this to write all IDProperties? Seems like that would actually simplify this code, and we'd get that feature of not losing custom properties for free too

What do you think about generalizing this to write all IDProperties? Seems like that would actually simplify this code, and we'd get that feature of not losing custom properties for free too
Author
Member

Generally a nice idea. We already have code for that too (convert_to_serialize_values). Unfortunately, it seems to store a fair amount of additional data that we don't really need here: https://archive.blender.org/developer/D12990

Not sure if we should bloat up the json data when it doesn't offer functional benefits for materials.
Some changes to existing code would be necessary as well, because it does not support IDP_IDARRAY.

Generally a nice idea. We already have code for that too (`convert_to_serialize_values`). Unfortunately, it seems to store a fair amount of additional data that we don't really need here: https://archive.blender.org/developer/D12990 Not sure if we should bloat up the json data when it doesn't offer functional benefits for materials. Some changes to existing code would be necessary as well, because it does not support `IDP_IDARRAY`.
JacquesLucke marked this conversation as resolved
@ -3720,0 +3814,4 @@
/** \} */
/* ------------------------------------------------------------------- */
/** \name Remove id mapping in geometry nodes modifier
Member

Title case

Title case
JacquesLucke marked this conversation as resolved
@ -3720,0 +3882,4 @@
/** \} */
/* ------------------------------------------------------------------- */
/** \name Add id mapping in geometry nodes modifier
Member

Title case

Title case
JacquesLucke marked this conversation as resolved
@ -2326,6 +2326,14 @@ typedef struct NodesModifierSettings {
struct IDProperty *properties;
} NodesModifierSettings;
typedef struct NodesModifierIDMapping {
Member

Might as well mention how this corresponds to BakeIDMappingKey here, or maybe the other way around.

Might as well mention how this corresponds to `BakeIDMappingKey` here, or maybe the other way around.
JacquesLucke marked this conversation as resolved
@ -2335,2 +2343,4 @@
*/
char *simulation_bake_directory;
int id_mappings_num;
Member

Add a high-level comment here:

/**
 * Name based storage of data-blocks in permanent bakes, so data-blocks aren't removed when reloading files.
 */

Or something like that :)

Add a high-level comment here: ``` /** * Name based storage of data-blocks in permanent bakes, so data-blocks aren't removed when reloading files. */ ``` Or something like that :)
JacquesLucke marked this conversation as resolved
@ -7057,0 +7108,4 @@
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_pointer_funcs(
prop, nullptr, nullptr, "rna_NodesModifier_id_mapping_id_typef", nullptr);
RNA_def_property_ui_text(prop, "ID", "");
Member

Data-Block

`Data-Block`
JacquesLucke marked this conversation as resolved
@ -7057,0 +7141,4 @@
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "active_id_mapping");
RNA_def_property_ui_text(prop, "Active ID Mapping Index", "");
Member

Data-Block

`Data-Block`
JacquesLucke marked this conversation as resolved
@ -1405,0 +1483,4 @@
RNA_pointer_create(
ptr->owner_id, &RNA_NodesModifierIDMapping, &active_mapping, &active_mapping_ptr);
uiTemplateAnyID(col, &active_mapping_ptr, "id", "id_type", "ID");
Member

Based on the screenshot, this is missing

  uiLayoutSetPropSep(col, true);
  uiLayoutSetPropDecorate(col, false);
Based on the screenshot, this is missing ``` uiLayoutSetPropSep(col, true); uiLayoutSetPropDecorate(col, false); ```
Author
Member

Just adding this looks a bit bad in the ID row with the extra type dropdown unfortunately.
Maybe the dropdown should be moved to a separate line, or hidden for now since we only use this for materials currently.

Just adding this looks a bit bad in the ID row with the extra type dropdown unfortunately. Maybe the dropdown should be moved to a separate line, or hidden for now since we only use this for materials currently.
Member

Yeah, uiTemplateAnyID looks a bit ancient, it was never updated for the property split change. I think I'd recommend just taking the equivalent of the two uiItemFullR calls in there. I started messing with that but didn't quite finish

Yeah, `uiTemplateAnyID` looks a bit ancient, it was never updated for the property split change. I think I'd recommend just taking the equivalent of the two `uiItemFullR` calls in there. I started messing with that but didn't quite finish
JacquesLucke marked this conversation as resolved
@ -1475,2 +1574,4 @@
IDP_BlendDataRead(reader, &nmd->settings.properties);
}
BLO_read_data_address(reader, &nmd->id_mappings);
for (const NodesModifierIDMapping &mapping : MutableSpan(nmd->id_mappings, nmd->id_mappings_num))
Member

const NodesModifierIDMapping -> NodesModifierIDMapping I think?

`const NodesModifierIDMapping` -> `NodesModifierIDMapping` I think?
Author
Member

Yeah, C casts are fun :D

Yeah, C casts are fun :D
JacquesLucke marked this conversation as resolved
Author
Member

Could the bake operator create the mapping maybe?

That can help, but I wonder if this implicit behavior is too much magic or not...

> Could the bake operator create the mapping maybe? That can help, but I wonder if this implicit behavior is too much magic or not...
Jacques Lucke added 8 commits 2023-07-14 13:55:55 +02:00
Hans Goudey reviewed 2023-07-14 18:04:55 +02:00
Hans Goudey left a comment
Member

That can help, but I wonder if this implicit behavior is too much magic or not...

If we can make it so that people don't have to deal with this list UI at all, we should take that opportunity IMO. This is the sort of thing people expect to "just work" unless they're aware of the implementation details.

>That can help, but I wonder if this implicit behavior is too much magic or not... If we can make it so that people don't have to deal with this list UI at all, we should take that opportunity IMO. This is the sort of thing people expect to "just work" unless they're aware of the implementation details.
@ -7057,0 +7113,4 @@
prop = RNA_def_property(srna, "id_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_id_type_items);
RNA_def_property_enum_default(prop, ID_MA);
Member

This property probably shouldn't be animate-able

This property probably shouldn't be animate-able
JacquesLucke marked this conversation as resolved
@ -1405,0 +1488,4 @@
ptr->owner_id, &RNA_NodesModifierIDMapping, &active_mapping, &active_mapping_ptr);
uiTemplateAnyID(col, &active_mapping_ptr, "id", "id_type", "ID");
uiItemR(col, &active_mapping_ptr, "id_name", 0, "ID Name", ICON_NONE);
Member

Heh, "ID" is hard to get rid of! Here too

Also, don't forget IFACE_ is necessary in UI code when manually specifying a label

Heh, "ID" is hard to get rid of! Here too Also, don't forget `IFACE_` is necessary in UI code when manually specifying a label
JacquesLucke marked this conversation as resolved
Jacques Lucke added 2 commits 2023-09-08 16:03:40 +02:00
Jacques Lucke added 2 commits 2023-09-08 16:10:13 +02:00
Jacques Lucke added 5 commits 2023-09-14 14:46:49 +02:00
Jacques Lucke added 1 commit 2023-09-25 15:34:57 +02:00
Jacques Lucke added 1 commit 2023-09-25 18:21:36 +02:00
Member

I kind of agree with Hans that ideally in most common cases the user shouldn't have to touch or even know about the existence of this panel. It feels really powerful and I like how this translates into remapping any possible datablock. But it really feels like it shouldn't be a necessity to set this up for common use-cases.

I'm not sure what the proper solution would be, but it seems like the update button is working quite well. Would it be an option to have a toggle for a 'coupled' mode that would run the operator automatically when the data isn't baked? Then you could still turn that off to have all the manual control.

I kind of agree with Hans that ideally in most common cases the user shouldn't have to touch or even know about the existence of this panel. It feels really powerful and I like how this translates into remapping any possible datablock. But it really feels like it shouldn't be a necessity to set this up for common use-cases. I'm not sure what the proper solution would be, but it seems like the update button is working quite well. Would it be an option to have a toggle for a 'coupled' mode that would run the operator automatically when the data isn't baked? Then you could still turn that off to have all the manual control.
Author
Member

Not sure what "coupled" means here exactly. If we want to automate this more, the main question for me is, what user actions should trigger the refresh of the mapping? It feels like it should be a well defined set of operators that also update the mapping, instead of running it all the time magically.

Not sure what "coupled" means here exactly. If we want to automate this more, the main question for me is, what user actions should trigger the refresh of the mapping? It feels like it should be a well defined set of operators that also update the mapping, instead of running it all the time magically.
Member

Not sure what "coupled" means here exactly.

I just used that for lack of a better word. I guess auto-sync would be more accurate.

If we want to automate this more, the main question for me is, what user actions should trigger the refresh of the mapping?

That's the tricky part. Off the top of my head I don't have that list. Ideally it would just work consistently whenever the list of datablocks referenced by the simulation data changes. But this could even happen at any time throughout the simulation dynamically right? And I don't think there would be a way to distinguish those types of changes.

I'm not sure how exactly the update operator works and how smart it is, but in theory this would need to run on all kinds of operations with all dependencies of the object to be safe, right?

> Not sure what "coupled" means here exactly. I just used that for lack of a better word. I guess auto-sync would be more accurate. > If we want to automate this more, the main question for me is, what user actions should trigger the refresh of the mapping? That's the tricky part. Off the top of my head I don't have that list. Ideally it would just work consistently whenever the list of datablocks referenced by the simulation data changes. But this could even happen at any time throughout the simulation dynamically right? And I don't think there would be a way to distinguish those types of changes. I'm not sure how exactly the update operator works and how smart it is, but in theory this would need to run on all kinds of operations with all dependencies of the object to be safe, right?
Author
Member

Correct. The operator works by using logged data from the previous evaluations of the node tree. During evaluation the simulation zone logs the name of materials that were used. That list is stored as a cache internally. When the refresh button is pressed, the mappings for the names in the list are created.

I'm not sure whether you think it would be ok to if we just show to the user when data blocks are missing. For example, the node could just have a warning. Would be nice if the warning could also have a button for the refresh, but not sure how that would look like right now.

Correct. The operator works by using logged data from the previous evaluations of the node tree. During evaluation the simulation zone logs the name of materials that were used. That list is stored as a cache internally. When the refresh button is pressed, the mappings for the names in the list are created. I'm not sure whether you think it would be ok to if we just show to the user when data blocks are missing. For example, the node could just have a warning. Would be nice if the warning could also have a button for the refresh, but not sure how that would look like right now.
Member

That logic sounds pretty sensible and solid.

But if this information is tracked anyways, what's the issue with just running the update whenever this warning would show up automatically? So whenever datablocks go missing, just update. If the missing data cannot be resolved, show the warning. And then making manual changes would require the user to turn off the auto-sync option.

Could that work?

That logic sounds pretty sensible and solid. But if this information is tracked anyways, what's the issue with just running the update whenever this warning would show up automatically? So whenever datablocks go missing, just update. If the missing data cannot be resolved, show the warning. And then making manual changes would require the user to turn off the auto-sync option. Could that work?
Author
Member

The problem is described in #108410. It's a very unconventional thing to change data like this on original objects, while the depsgraph is evaluated. Also not sure how that would work with undo. Not to mention that adding the mapping would require the depsgraph to be evaluated again after the materials are all added to it. Tagging the depsgraph during evaluation is something we probably don't do anywhere yet.

The problem is described in #108410. It's a very unconventional thing to change data like this on original objects, while the depsgraph is evaluated. Also not sure how that would work with undo. Not to mention that adding the mapping would require the depsgraph to be evaluated again after the materials are all added to it. Tagging the depsgraph during evaluation is something we probably don't do anywhere yet.
Member

Okay, I see. Hmm, I do feel quite strongly that the user shouldn't have to interact with this in most cases. But I don't have a solution to that either. Let's discuss this more tomorrow, as this really is something that should be figured out for 4.0 ...

Okay, I see. Hmm, I do feel quite strongly that the user shouldn't have to interact with this in most cases. But I don't have a solution to that either. Let's discuss this more tomorrow, as this really is something that should be figured out for 4.0 ...
Member

I showed this to @Sergey

He mentioned that this seems like it's very close to implementing something like layering as it exists in USD, so there might be some synergy there, should probably be done in a consistent way.
Also maybe for @brecht

In terms of depsgraph, he said it's not immediately clear why you would have to trigger a rebuild on an update, as all the dependencies should already be part of the geometry at that point, right?

He also proposed to nudge down the amount of low level user access, by not showing the db and lib name as editable but read-only.

I showed this to @Sergey He mentioned that this seems like it's very close to implementing something like layering as it exists in USD, so there might be some synergy there, should probably be done in a consistent way. Also maybe for @brecht In terms of depsgraph, he said it's not immediately clear why you would have to trigger a rebuild on an update, as all the dependencies should already be part of the geometry at that point, right? He also proposed to nudge down the amount of low level user access, by not showing the db and lib name as editable but read-only.
Author
Member

He mentioned that this seems like it's very close to implementing something like layering as it exists in USD, so there might be some synergy there, should probably be done in a consistent way.

Could be, don't know what's going on there exactly.

In terms of depsgraph, he said it's not immediately clear why you would have to trigger a rebuild on an update, as all the dependencies should already be part of the geometry at that point, right?

They probably are in the depsgraph already, yes. Still, the depsgraph would be out of date because it's missing some relations. I'm not aware of any other situation where this would be the case. Also, it's not entirely clear to me if the mapping should also be created from non-active depsgraph like for rendering.

He also proposed to nudge down the amount of low level user access, by not showing the db and lib name as editable but read-only.

I think being able to edit those is quite important in general when things have been renamed and you still want to use an existing cache. Not sure if we can make them read-only by default, or if that would really help.

> He mentioned that this seems like it's very close to implementing something like layering as it exists in USD, so there might be some synergy there, should probably be done in a consistent way. Could be, don't know what's going on there exactly. > In terms of depsgraph, he said it's not immediately clear why you would have to trigger a rebuild on an update, as all the dependencies should already be part of the geometry at that point, right? They probably are in the depsgraph already, yes. Still, the depsgraph would be out of date because it's missing some relations. I'm not aware of any other situation where this would be the case. Also, it's not entirely clear to me if the mapping should also be created from non-active depsgraph like for rendering. > He also proposed to nudge down the amount of low level user access, by not showing the db and lib name as editable but read-only. I think being able to edit those is quite important in general when things have been renamed and you still want to use an existing cache. Not sure if we can make them read-only by default, or if that would really help.
Member

Can't really speak on the first 2 topics.

I think being able to edit those is quite important in general when things have been renamed and you still want to use an existing cache. Not sure if we can make them read-only by default, or if that would really help.

Maybe I'm wrong, but shouldn't that still work? You would rename the datablock in the file, not in the cache itself. If it's baked you don't touch the list itself, but the assigned datablock gets renamed. I guess if it's not baked you would need to update the name to keep it up to date and deduplicate with frames that are yet to be cached (?). But it wouldn't be terrible if in this case there are 2 entries mapping to the same datablock imo.

But maybe I'm missing something (?)

Can't really speak on the first 2 topics. > I think being able to edit those is quite important in general when things have been renamed and you still want to use an existing cache. Not sure if we can make them read-only by default, or if that would really help. Maybe I'm wrong, but shouldn't that still work? You would rename the datablock in the file, not in the cache itself. If it's baked you don't touch the list itself, but the assigned datablock gets renamed. I guess if it's not baked you would need to update the name to keep it up to date and deduplicate with frames that are yet to be cached (?). But it wouldn't be terrible if in this case there are 2 entries mapping to the same datablock imo. But maybe I'm missing something (?)
Author
Member

Maybe I'm wrong, but shouldn't that still work? You would rename the datablock in the file, not in the cache itself. If it's baked you don't touch the list itself, but the assigned datablock gets renamed. I guess if it's not baked you would need to update the name to keep it up to date and deduplicate with frames that are yet to be cached (?). But it wouldn't be terrible if in this case there are 2 entries mapping to the same datablock imo.

Yes, I think it would work fine for the large majority of use cases we run into nowadays. I'm more concerned about the case when we load baked data that has been baked independently. Guess that's less of a use case so far, because we don't have the Import Bake node (or other import nodes for that matter) yet. I'm fine with making them read-only for now, and worry about that again in the future. I'm not entirely sure what we win by making them read-only instead of leaving them editable or just graying them out.

> Maybe I'm wrong, but shouldn't that still work? You would rename the datablock in the file, not in the cache itself. If it's baked you don't touch the list itself, but the assigned datablock gets renamed. I guess if it's not baked you would need to update the name to keep it up to date and deduplicate with frames that are yet to be cached (?). But it wouldn't be terrible if in this case there are 2 entries mapping to the same datablock imo. Yes, I think it would work fine for the large majority of use cases we run into nowadays. I'm more concerned about the case when we load baked data that has been baked independently. Guess that's less of a use case so far, because we don't have the Import Bake node (or other import nodes for that matter) yet. I'm fine with making them read-only for now, and worry about that again in the future. I'm not entirely sure what we win by making them read-only instead of leaving them editable or just graying them out.

To me there are two separate aspects here:

  • Preserve the IDs throughout the cached simulation
  • Ability to override data which is streamed from a cache

Only the latter step is where there seems to be overlap with what one expects from USD and layering integration, and where some alignment between teams could be very good. I am not sure if this is something considered an integral part of the caching project which is aimed to be solved for Blender 4.0.

If the scope can be narrowed to only preservation ID assignment throughout the cache then I am not sure why we need to go into this user level complexity. The library path + ID name is sufficient to identify data-block within a .blend file. So storing them in the cache should suffice for the purpose of mapping.

Surely material can be renamed, but then the cache technically becomes out-dated. And to me the recovery from this is to re-cache instead of going to some editor and re-assigning materials in the mapping.

Still, the depsgraph would be out of date because it's missing some relations

The only case when it will happen is if the cache is read is a different .blend file which does not contain the entire node tree. I do not know if this is the intended use of the system at this time.

If the cache is consistent with the current state of the tree then all the relations should already be in the dpesgrph. If the cache is not consistent with the state of the tree then it should be re-cached.

To me there are two separate aspects here: * Preserve the IDs throughout the cached simulation * Ability to override data which is streamed from a cache Only the latter step is where there seems to be overlap with what one expects from USD and layering integration, and where some alignment between teams could be very good. I am not sure if this is something considered an integral part of the caching project which is aimed to be solved for Blender 4.0. If the scope can be narrowed to only preservation ID assignment throughout the cache then I am not sure why we need to go into this user level complexity. The library path + ID name is sufficient to identify data-block within a .blend file. So storing them in the cache should suffice for the purpose of mapping. Surely material can be renamed, but then the cache technically becomes out-dated. And to me the recovery from this is to re-cache instead of going to some editor and re-assigning materials in the mapping. > Still, the depsgraph would be out of date because it's missing some relations The only case when it will happen is if the cache is read is a different .blend file which does not contain the entire node tree. I do not know if this is the intended use of the system at this time. If the cache is consistent with the current state of the tree then all the relations should already be in the dpesgrph. If the cache is not consistent with the state of the tree then it should be re-cached.
Member

I'm not entirely sure what we win by making them read-only instead of leaving them editable or just graying them out.

I think it's mainly about presenting the relevant information to the user in a way that is least confusing and without committing to this being the way that we handle overriding/layering data more generically.

> I'm not entirely sure what we win by making them read-only instead of leaving them editable or just graying them out. I think it's mainly about presenting the relevant information to the user in a way that is least confusing and without committing to this being the way that we handle overriding/layering data more generically.
Author
Member

I'm fine with ignoring the use case of overriding used IDs for now.

If the cache is consistent with the current state of the tree then all the relations should already be in the depsgraph. If the cache is not consistent with the state of the tree then it should be re-cached.

I don't quite agree with that statement. If you bake something, then you should still be able to use the baked data even if some of the inputs to the simulation changed. Until then, loading the baked data including making links to data blocks should work. It's up to the user when he wants to bake again. Otherwise, all many tiny changes in .blend files will make bakes invalid.

Also, just looking up IDs by name and library name during depsgraph evaluation seems wrong to me, especially if we can't be 100% sure that the data block is in the depsgraph and it's evaluated before the current object (which would be the case if we just use the name strings stored in the baked data). Are we doing that anywhere else?

I'm not entirely sure if you (@Sergey) were proposing not to store the mapping in the modifier explicitly, but to do something else instead, or if you were just arguing that we don't need to give the user the ability to change the mapping names yet.

I'm fine with ignoring the use case of overriding used IDs for now. > If the cache is consistent with the current state of the tree then all the relations should already be in the depsgraph. If the cache is not consistent with the state of the tree then it should be re-cached. I don't quite agree with that statement. If you bake something, then you should still be able to use the baked data even if some of the inputs to the simulation changed. Until then, loading the baked data including making links to data blocks should work. It's up to the user when he wants to bake again. Otherwise, all many tiny changes in .blend files will make bakes invalid. Also, just looking up IDs by name and library name during depsgraph evaluation seems wrong to me, especially if we can't be 100% sure that the data block is in the depsgraph and it's evaluated before the current object (which would be the case if we just use the name strings stored in the baked data). Are we doing that anywhere else? I'm not entirely sure if you (@Sergey) were proposing not to store the mapping in the modifier explicitly, but to do something else instead, or if you were just arguing that we don't need to give the user the ability to change the mapping names yet.
Jacques Lucke requested review from Brecht Van Lommel 2023-09-26 17:00:26 +02:00
Jacques Lucke requested review from Sergey Sharybin 2023-09-26 17:00:26 +02:00

We discussed this in a meeting. My notes.

  • My concern was that this potentially introduces another kind of library linking, where the bake/cache system has its own way of resolving references to datablocks separate from the existing library linking. However the PR only really uses the datablock name + library name (not filepath) as a unique identifier to associate a name in the external cache file with a key in the mapping. So in that design that is not really a problem.
  • This does raise the question of how to share caches between .blend files. We agreed that the external cache files should certainly not contain the material datablock itself and only reference it, but the question is how. The external cache files could contain a complete reference to datablocks (library filepath + datablock name), but this would be another library linking implementation which we want to avoid. The more likely options seem to be a cache datablock that contains the mapping and can bring along material datablocks using regular library linking. The external cache files could also be loaded without mapping and let the users manually assign materials, but this may be tedious. In any case, figuring out that design is not a blocker for this PR.
  • For usability we think the mapping really should be automatically updated. We went over the implications of this, and it seems to be quite safe as far as the depsgraph is concerned. We don't have to rebuild the depsgraph when the mapping is updated, because all the relevant datablocks should already be in the depsgraph, even if some relations would be different after rebuild. Effectively we want to update the mapping along with caching a new frame.
  • The tricky case with auto updated mapping is the undo system. For undo writing it's not a problem to write the mapping each time, it should not cause unnecessary depsgraph updates. But there can be a problem where the mapping is incomplete, when you change the frame, a material gets added to the mapping, and then you undo. Then the mapping would be restored from the previous state and be empty, while the external cache files still references a material. To avoid this perhaps on undo restore the union of the current and restored mapping could be taken. Unless the current cache and mapping was cleared and not repopulated yet, in which case it could remain empty.
  • We don't want the datablock name and library name to be user editable. This should not be needed if the mapping is auto updated, and rather obscure. Allowing the datablock pointer to be edited seems ok. For example it would make sense for the Remap Users operators to be able to replace both the material on the original mesh and on the bake.
We discussed this in a meeting. My notes. * My concern was that this potentially introduces another kind of library linking, where the bake/cache system has its own way of resolving references to datablocks separate from the existing library linking. However the PR only really uses the datablock name + library name (not filepath) as a unique identifier to associate a name in the external cache file with a key in the mapping. So in that design that is not really a problem. * This does raise the question of how to share caches between .blend files. We agreed that the external cache files should certainly not contain the material datablock itself and only reference it, but the question is how. The external cache files could contain a complete reference to datablocks (library filepath + datablock name), but this would be another library linking implementation which we want to avoid. The more likely options seem to be a cache datablock that contains the mapping and can bring along material datablocks using regular library linking. The external cache files could also be loaded without mapping and let the users manually assign materials, but this may be tedious. In any case, figuring out that design is not a blocker for this PR. * For usability we think the mapping really should be automatically updated. We went over the implications of this, and it seems to be quite safe as far as the depsgraph is concerned. We don't have to rebuild the depsgraph when the mapping is updated, because all the relevant datablocks should already be in the depsgraph, even if some relations would be different after rebuild. Effectively we want to update the mapping along with caching a new frame. * The tricky case with auto updated mapping is the undo system. For undo writing it's not a problem to write the mapping each time, it should not cause unnecessary depsgraph updates. But there can be a problem where the mapping is incomplete, when you change the frame, a material gets added to the mapping, and then you undo. Then the mapping would be restored from the previous state and be empty, while the external cache files still references a material. To avoid this perhaps on undo restore the union of the current and restored mapping could be taken. Unless the current cache and mapping was cleared and not repopulated yet, in which case it could remain empty. * We don't want the datablock name and library name to be user editable. This should not be needed if the mapping is auto updated, and rather obscure. Allowing the datablock pointer to be edited seems ok. For example it would make sense for the Remap Users operators to be able to replace both the material on the original mesh and on the bake.

A concern that came up when talking about this with @SimonThommes. Is the mapping per cache/bake? If so, for multiple simulation zones that would cause problems for auto updating. Since you wouldn't be able to clear the whole cache when e.g. one simulation zone is baked and the other is getting cleared.

For the UI, the layout is a bit confusing to me as part of the "Internal Dependencies" panel. I would expect this to be part of a Bake/Cache panel, maybe a subpanel named "Referenced Data-blocks" or "Used Data-blocks" or something like that. From looking at the screenshot in the description, it wouldn't be obvious to me that this is only about simulation and caching/baking.

A concern that came up when talking about this with @SimonThommes. Is the mapping per cache/bake? If so, for multiple simulation zones that would cause problems for auto updating. Since you wouldn't be able to clear the whole cache when e.g. one simulation zone is baked and the other is getting cleared. For the UI, the layout is a bit confusing to me as part of the "Internal Dependencies" panel. I would expect this to be part of a Bake/Cache panel, maybe a subpanel named "Referenced Data-blocks" or "Used Data-blocks" or something like that. From looking at the screenshot in the description, it wouldn't be obvious to me that this is only about simulation and caching/baking.
Author
Member

All of that sounds reasonable to me, except for the sentence below which is ambiguous (it's missing is or is not).

In any case, figuring out that design a blocker for this PR.

All of that sounds reasonable to me, except for the sentence below which is ambiguous (it's missing `is` or `is not`). > In any case, figuring out that design a blocker for this PR.
Author
Member

A concern that came up when talking about this with SimonThommes. Is the mapping per cache/bake? If for multiple that would cause problems for auto updating, since you wouldn't be able to clear the whole cache when e.g. one simulation zone is baked and the other is getting cleared.

I addressed this in the original patch description already and also just talked about this with Simon.

Currently, there is one mapping per modifier. In theory, the mapping could also exist at higher (e.g. per object) and at lower levels (e.g. per simulation zone). Having it in the modifier, shouldn't prevent us from having the mapping in other places in the future.

Basically, I think it's totally reasonable to store the mapping at different levels depending on the use case, but I think storing it on the modifier is a reasonable way to start.

> A concern that came up when talking about this with SimonThommes. Is the mapping per cache/bake? If for multiple that would cause problems for auto updating, since you wouldn't be able to clear the whole cache when e.g. one simulation zone is baked and the other is getting cleared. I addressed this in the original patch description already and also just talked about this with Simon. > Currently, there is one mapping per modifier. In theory, the mapping could also exist at higher (e.g. per object) and at lower levels (e.g. per simulation zone). Having it in the modifier, shouldn't prevent us from having the mapping in other places in the future. Basically, I think it's totally reasonable to store the mapping at different levels depending on the use case, but I think storing it on the modifier is a reasonable way to start.
Author
Member

I just noticed one issue we didn't talk about before. When adding the mapping on original data during depsgraph evaluation, I also have to increase the user count (id_us_plus). However, doing that is not thread-safe. Will just ignore this race condition for now and keep going, but it's something to keep in mind.

I just noticed one issue we didn't talk about before. When adding the mapping on original data during depsgraph evaluation, I also have to increase the user count (`id_us_plus`). However, doing that is not thread-safe. Will just ignore this race condition for now and keep going, but it's something to keep in mind.
Jacques Lucke added 9 commits 2023-09-26 20:12:10 +02:00
Jacques Lucke added 3 commits 2023-09-26 20:40:23 +02:00
Jacques Lucke added 2 commits 2023-09-27 10:27:56 +02:00
Simon Thommes requested changes 2023-09-27 12:19:01 +02:00
Simon Thommes left a comment
Member

Generally for me this is working as expected. Testing this a bit more in-depth, the ability to manually change this mapping has some further implications that are not immediately clear.

I have a few proposals:

  • I agree with Brecht, that from the panel it is not clear that this is used for the cache only. So either the panel name or behavior should change. Probably the name to something like Cache Data Block Mapping
  • When making manual changes to the mapping it is a bit too unclear to me how to go back to a clean slate. I think there should be an Update or Clear button that gets rid of all elements and rebuild the list using the automatic functionality.
  • An issue that I found is that the list grows automatically but unused data blocks don't get removed anymore. That means that you end up with virtual users of these material data blocks that are just used in the mapping and nowhere else in the scene, which has to be cleaned up manually

All in all, considering everything , this might be too much to still validate properly for 4.0 in the scope currently aimed at.

In my personal opinion, for 4.0 it would already be useful in a state where you cannot edit this list at all. Then it's always just a reflection of what has been cached and Blender manages it autonomously. This would still work fine in most common cases and in failing cases there is at least some insight why you might need to re-cache.
But that would rely on always keeping the list in sync with the data blocks that are actually being used.

What do you think?

Generally for me this is working as expected. Testing this a bit more in-depth, the ability to manually change this mapping has some further implications that are not immediately clear. I have a few proposals: - I agree with Brecht, that from the panel it is not clear that this is used for the cache only. So either the panel name or behavior should change. Probably the name to something like `Cache Data Block Mapping` - When making manual changes to the mapping it is a bit too unclear to me how to go back to a clean slate. I think there should be an `Update` or `Clear` button that gets rid of all elements and rebuild the list using the automatic functionality. - An issue that I found is that the list grows automatically but unused data blocks don't get removed anymore. That means that you end up with virtual users of these material data blocks that are just used in the mapping and nowhere else in the scene, which has to be cleaned up manually All in all, considering everything , this might be too much to still validate properly for 4.0 in the scope currently aimed at. In my personal opinion, for 4.0 it would already be useful in a state where you cannot edit this list at all. Then it's always just a reflection of what has been cached and Blender manages it autonomously. This would still work fine in most common cases and in failing cases there is at least some insight why you might need to re-cache. But that would rely on always keeping the list in sync with the data blocks that are actually being used. What do you think?
Author
Member

We are in bcon3 already. This won't be part of 4.0.

When making manual changes to the mapping it is a bit too unclear to me how to go back to a clean slate.

Yes, mentioned that in the patch description already.

Automatically removing mappings is quite a bit more challenging in general. We can still make some useful utilities though. One also has to be much more careful with automatically deleting data than with creating data, even if the data that is deleted was generated.

We are in bcon3 already. This won't be part of 4.0. > When making manual changes to the mapping it is a bit too unclear to me how to go back to a clean slate. Yes, mentioned that in the patch description already. Automatically removing mappings is quite a bit more challenging in general. We can still make some useful utilities though. One also has to be much more careful with automatically deleting data than with creating data, even if the data that is deleted was generated.
Member

Yes, mentioned that in the patch description already.

Ah sorry, I missed your update to that.

> Yes, mentioned that in the patch description already. Ah sorry, I missed your update to that.
First-time contributor

Hi, is this going to be re-implemented before geo-node development takes a back seat? just i see people need this!? it would be a positive for the community and for geo-nodes to move ahead with proponents being able to utilise it.

Hi, is this going to be re-implemented before geo-node development takes a back seat? just i see people need this!? it would be a positive for the community and for geo-nodes to move ahead with proponents being able to utilise it.
Author
Member

Closing this in favor of #117043.

Closing this in favor of #117043.
Jacques Lucke closed this pull request 2024-01-11 20:26:31 +01:00

Pull request closed

Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset System
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Asset Browser Project
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No Assignees
6 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#109703
No description provided.