Core: Fix/refactor invalid logic around handling of 'default materials'.

These were handled mostly completely outside of IDManagement code, yet
(ab)using the same ID management system in some cases, adding hacks to
address some issues, etc.

Also address a similar issue in the eevee lookdev `LookdevWorld` code.

Since initializing pre-allocated (or static) buffers as valid IDs is not
currently supported, and these use-cases do not seem common enough to be
worth supporting it currently, instead switch to storing allocated IDs
into static pointers.

This allows to use proper 'out-of-main' ID creation code API.

NOTE: There are still some remaining issues, especially in the GP
material BKE api. These are noted in comments for now, as it would be
out of scope to address them in this commit.

Pull Request: https://projects.blender.org/blender/blender/pulls/138263
This commit is contained in:
Bastien Montagne
2025-05-02 14:09:53 +02:00
committed by Bastien Montagne
parent 58247efc69
commit 368c737fe7
6 changed files with 76 additions and 60 deletions

View File

@@ -215,6 +215,11 @@ void ramp_blend(int type, float r_col[3], float fac, const float col[3]);
/* -------------------------------------------------------------------- */
/** \name Default Materials
*
* TODO: Explain expected usages? Seems to be primarily defined for GPU/viewport code?
*
* \warning _NEVER_ use these materials as fallback data for regular ID data. They should only be
* used as template/copy source, or in some very specific, local and short-lived contexts.
* \{ */
Material *BKE_material_default_empty();

View File

@@ -2589,6 +2589,8 @@ Material *BKE_grease_pencil_object_material_ensure_from_brush(Main *bmain,
}
/* Fall back to default material. */
/* XXX FIXME This is critical abuse of the 'default material' feature, these IDs should never be
* used/returned as 'regular' data. */
return BKE_material_default_gpencil();
}

View File

@@ -88,8 +88,6 @@ static void material_init_data(ID *id)
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(material, id));
MEMCPY_STRUCT_AFTER(material, DNA_struct_default_get(Material), id);
*((short *)id->name) = ID_MA;
}
static void material_copy_data(Main *bmain,
@@ -937,6 +935,8 @@ Material *BKE_gpencil_material(Object *ob, short act)
return ma;
}
/* XXX FIXME This is critical abuse of the 'default material' feature, these IDs should never be
* used/returned as 'regular' data. */
return BKE_material_default_gpencil();
}
@@ -2016,29 +2016,37 @@ void BKE_material_eval(Depsgraph *depsgraph, Material *material)
* Used for rendering when objects have no materials assigned, and initializing
* default shader nodes. */
static Material default_material_empty;
static Material default_material_holdout;
static Material default_material_surface;
static Material default_material_volume;
static Material default_material_gpencil;
static Material *default_material_empty = nullptr;
static Material *default_material_holdout = nullptr;
static Material *default_material_surface = nullptr;
static Material *default_material_volume = nullptr;
static Material *default_material_gpencil = nullptr;
static Material *default_materials[] = {&default_material_empty,
&default_material_holdout,
&default_material_surface,
&default_material_volume,
&default_material_gpencil,
nullptr};
static Material **default_materials[] = {&default_material_empty,
&default_material_holdout,
&default_material_surface,
&default_material_volume,
&default_material_gpencil,
nullptr};
static void material_default_gpencil_init(Material *ma)
static Material *material_default_create(Material **ma_p, const char *name)
{
BLI_strncpy(ma->id.name + 2, "Default GPencil", MAX_NAME);
*ma_p = static_cast<Material *>(
BKE_libblock_alloc_in_lib(nullptr, std::nullopt, ID_MA, name, LIB_ID_CREATE_NO_MAIN));
return *ma_p;
}
static void material_default_gpencil_init(Material **ma_p)
{
Material *ma = material_default_create(ma_p, "Default GPencil");
BKE_gpencil_material_attr_init(ma);
add_v3_fl(&ma->gp_style->stroke_rgba[0], 0.6f);
}
static void material_default_surface_init(Material *ma)
static void material_default_surface_init(Material **ma_p)
{
BLI_strncpy(ma->id.name + 2, "Default Surface", MAX_NAME);
Material *ma = material_default_create(ma_p, "Default Surface");
bNodeTree *ntree = blender::bke::node_tree_add_tree_embedded(
nullptr, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
@@ -2064,9 +2072,9 @@ static void material_default_surface_init(Material *ma)
blender::bke::node_set_active(*ntree, *output);
}
static void material_default_volume_init(Material *ma)
static void material_default_volume_init(Material **ma_p)
{
BLI_strncpy(ma->id.name + 2, "Default Volume", MAX_NAME);
Material *ma = material_default_create(ma_p, "Default Volume");
bNodeTree *ntree = blender::bke::node_tree_add_tree_embedded(
nullptr, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
@@ -2090,9 +2098,9 @@ static void material_default_volume_init(Material *ma)
blender::bke::node_set_active(*ntree, *output);
}
static void material_default_holdout_init(Material *ma)
static void material_default_holdout_init(Material **ma_p)
{
BLI_strncpy(ma->id.name + 2, "Default Holdout", MAX_NAME);
Material *ma = material_default_create(ma_p, "Default Holdout");
bNodeTree *ntree = blender::bke::node_tree_add_tree_embedded(
nullptr, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
@@ -2117,34 +2125,34 @@ static void material_default_holdout_init(Material *ma)
Material *BKE_material_default_empty()
{
return &default_material_empty;
return default_material_empty;
}
Material *BKE_material_default_holdout()
{
return &default_material_holdout;
return default_material_holdout;
}
Material *BKE_material_default_surface()
{
return &default_material_surface;
return default_material_surface;
}
Material *BKE_material_default_volume()
{
return &default_material_volume;
return default_material_volume;
}
Material *BKE_material_default_gpencil()
{
return &default_material_gpencil;
return default_material_gpencil;
}
void BKE_material_defaults_free_gpu()
{
for (int i = 0; default_materials[i]; i++) {
Material *ma = default_materials[i];
if (ma->gpumaterial.first) {
Material *ma = *default_materials[i];
if (ma && ma->gpumaterial.first) {
GPU_material_free(&ma->gpumaterial);
}
}
@@ -2155,13 +2163,12 @@ void BKE_material_defaults_free_gpu()
void BKE_materials_init()
{
for (int i = 0; default_materials[i]; i++) {
/* Note: material_init_data() expects input struct to be initialized at zero.
* However, we are initializing global static materials in this function, which will only get
* properly defined in the respective material_default_*_init() functions. */
memset(&default_materials[i]->id, 0, sizeof(Material));
material_init_data(&default_materials[i]->id);
BLI_assert_msg(*default_materials[i] == nullptr,
"Default material pointers should always be null when initializing them, maybe "
"missing a call to `BKE_materials_exit` first?");
}
material_default_create(&default_material_empty, "Default Empty");
material_default_surface_init(&default_material_surface);
material_default_volume_init(&default_material_volume);
material_default_holdout_init(&default_material_holdout);
@@ -2171,6 +2178,10 @@ void BKE_materials_init()
void BKE_materials_exit()
{
for (int i = 0; default_materials[i]; i++) {
material_free_data(&default_materials[i]->id);
Material *ma = *default_materials[i];
*default_materials[i] = nullptr;
if (ma) {
BKE_id_free(nullptr, &ma->id);
}
}
}

View File

@@ -284,9 +284,8 @@ PointCloud *BKE_pointcloud_add(Main *bmain, const char *name)
PointCloud *BKE_pointcloud_add_default(Main *bmain, const char *name)
{
PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(bmain, ID_PT, name, 0));
PointCloud *pointcloud = static_cast<PointCloud *>(BKE_id_new(bmain, ID_PT, name));
pointcloud_init_data(&pointcloud->id);
pointcloud_random(pointcloud);
return pointcloud;
@@ -297,7 +296,7 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(
nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE));
pointcloud_init_data(&pointcloud->id);
BKE_libblock_init_empty(&pointcloud->id);
CustomData_realloc(&pointcloud->pdata, 0, totpoint);
pointcloud->totpoint = totpoint;

View File

@@ -31,8 +31,11 @@ namespace blender::eevee {
LookdevWorld::LookdevWorld()
{
bNodeTree *ntree = bke::node_tree_add_tree(
nullptr, "Lookdev World Nodetree", ntreeType_Shader->idname);
/* Create a dummy World data block to hold the nodetree generated for studio-lights. */
world = static_cast<::World *>(BKE_id_new_nomain(ID_MA, "Lookdev"));
bNodeTree *ntree = bke::node_tree_add_tree_embedded(
nullptr, &world->id, "Lookdev World Nodetree", ntreeType_Shader->idname);
bNode *coordinate = bke::node_add_static_node(nullptr, *ntree, SH_NODE_TEX_COORD);
bNodeSocket *coordinate_out = bke::node_find_socket(*coordinate, SOCK_OUT, "Generated");
@@ -65,12 +68,14 @@ LookdevWorld::LookdevWorld()
bke::node_add_link(*ntree, *background, *background_out, *output, *output_in);
bke::node_set_active(*ntree, *output);
world->use_nodes = true;
world->nodetree = ntree;
/* Create a dummy image data block to hold GPU textures generated by studio-lights. */
STRNCPY(image.id.name, "IMLookdev");
BKE_libblock_init_empty(&image.id);
image.type = IMA_TYPE_IMAGE;
image.source = IMA_SRC_GENERATED;
ImageTile *base_tile = BKE_image_get_tile(&image, 0);
image = static_cast<::Image *>(BKE_id_new_nomain(ID_IM, "Lookdev"));
image->type = IMA_TYPE_IMAGE;
image->source = IMA_SRC_GENERATED;
ImageTile *base_tile = BKE_image_get_tile(image, 0);
base_tile->gen_x = 1;
base_tile->gen_y = 1;
base_tile->gen_type = IMA_GENTYPE_BLANK;
@@ -78,19 +83,13 @@ LookdevWorld::LookdevWorld()
/* TODO: This works around the issue that the first time the texture is accessed the image would
* overwrite the set GPU texture. A better solution would be to use image data-blocks as part of
* the studio-lights, but that requires a larger refactoring. */
BKE_image_get_gpu_texture(&image, &environment_storage->iuser);
/* Create a dummy image data block to hold GPU textures generated by studio-lights. */
STRNCPY(world.id.name, "WOLookdev");
BKE_libblock_init_empty(&world.id);
world.use_nodes = true;
world.nodetree = ntree;
BKE_image_get_gpu_texture(image, &environment_storage->iuser);
}
LookdevWorld::~LookdevWorld()
{
BKE_libblock_free_datablock(&image.id, 0);
BKE_libblock_free_datablock(&world.id, 0);
BKE_id_free(nullptr, &image->id);
BKE_id_free(nullptr, &world->id);
}
bool LookdevWorld::sync(const LookdevParameters &new_parameters)
@@ -101,7 +100,7 @@ bool LookdevWorld::sync(const LookdevParameters &new_parameters)
intensity_socket_->value = parameters_.intensity;
angle_socket_->value = parameters_.rot_z;
GPU_TEXTURE_FREE_SAFE(image.gputexture[TEXTARGET_2D][0]);
GPU_TEXTURE_FREE_SAFE(image->gputexture[TEXTARGET_2D][0]);
environment_node_->id = nullptr;
StudioLight *sl = BKE_studiolight_find(parameters_.hdri.c_str(),
@@ -111,12 +110,12 @@ bool LookdevWorld::sync(const LookdevParameters &new_parameters)
GPUTexture *texture = sl->equirect_radiance_gputexture;
if (texture != nullptr) {
GPU_texture_ref(texture);
image.gputexture[TEXTARGET_2D][0] = texture;
environment_node_->id = &image.id;
image->gputexture[TEXTARGET_2D][0] = texture;
environment_node_->id = &image->id;
}
}
GPU_material_free(&world.gpumaterial);
GPU_material_free(&world->gpumaterial);
}
return parameters_changed;
}

View File

@@ -72,8 +72,8 @@ class LookdevWorld {
bNode *environment_node_ = nullptr;
bNodeSocketValueFloat *intensity_socket_ = nullptr;
bNodeSocketValueFloat *angle_socket_ = nullptr;
::Image image = {};
::World world = {};
::Image *image = nullptr;
::World *world = nullptr;
LookdevParameters parameters_;
@@ -86,7 +86,7 @@ class LookdevWorld {
::World *world_get()
{
return &world;
return world;
}
float background_opacity_get() const