Nodes: Panels integration with blend files and UI #111348

Merged
Lukas Tönne merged 96 commits from LukasTonne/blender:node-panels-final into main 2023-08-30 12:37:28 +02:00
19 changed files with 146 additions and 54 deletions
Showing only changes of commit aa1446fb14 - Show all commits

View File

@ -108,6 +108,7 @@ const UserDef U_default = {
.gp_euclideandist = 2,
.gp_eraser = 25,
.gp_settings = 0,
.playback_fps_samples = 8,
#ifdef __APPLE__
.gpu_backend = GPU_BACKEND_METAL,
#else

View File

@ -720,6 +720,9 @@ class USERPREF_PT_viewport_display(ViewportPanel, CenterAlignMixIn, Panel):
col.prop(view, "show_object_info", text="Object Info")
col.prop(view, "show_view_name", text="View Name")
col.prop(view, "show_playback_fps", text="Playback Frame Rate (FPS)")
row = col.row()
row.active = view.show_playback_fps
row.prop(view, "playback_fps_samples", text="Frame Rate Samples")
layout.separator()

View File

@ -305,7 +305,7 @@ class LayerRuntime {
*/
Vector<LayerMask> masks_;
/* Runtime data used for frame transformations.*/
/* Runtime data used for frame transformations. */
LayerTransformData trans_data_;
};

View File

@ -604,7 +604,7 @@ GreasePencilFrame *Layer::add_frame(const FramesMapKey key,
return frame;
}
next_key_it = this->remove_leading_null_frames_in_range(next_key_it, sorted_keys.end());
/* If the duration is set to 0, the frame is marked as an implicit hold.*/
/* If the duration is set to 0, the frame is marked as an implicit hold. */
if (duration == 0) {
frame->flag |= GP_FRAME_IMPLICIT_HOLD;
return frame;

View File

@ -184,6 +184,7 @@ static void shapekey_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *i
/* ShapeKeys should always only be linked indirectly through their user ID (mesh, Curve etc.), or
* be fully local data. */
BLI_assert((id->tag & LIB_TAG_EXTERN) == 0);
UNUSED_VARS_NDEBUG(id);
}
IDTypeInfo IDType_ID_KE = {

View File

@ -433,11 +433,4 @@ class Array {
}
};
/**
* Same as a normal Array, but does not use Blender's guarded allocator. This is useful when
* allocating memory with static storage duration.
*/
template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
using RawArray = Array<T, InlineBufferCapacity, RawAllocator>;
} // namespace blender

View File

@ -497,7 +497,7 @@ void BLO_main_expander(BLOExpandDoitCallback expand_doit_func);
* Loop over all ID data in Main to mark relations.
* Set (id->tag & LIB_TAG_NEED_EXPAND) to mark expanding. Flags get cleared after expanding.
*
* \param fdhandle: usually file-data, or own handle.
* \param fdhandle: usually file-data, or own handle. May be nullptr.
* \param mainvar: the Main database to expand.
*/
void BLO_expand_main(void *fdhandle, struct Main *mainvar);

View File

@ -4148,7 +4148,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
* Expanding should never modify ID pointers themselves.
* Handling of DNA deprecated data should never be needed in undo case. */
const int flag = IDWALK_READONLY | IDWALK_NO_ORIG_POINTERS_ACCESS |
((fd->flags & FD_FLAGS_IS_MEMFILE) ? 0 : IDWALK_DO_DEPRECATED_POINTERS);
((!fd || (fd->flags & FD_FLAGS_IS_MEMFILE)) ?
0 :
IDWALK_DO_DEPRECATED_POINTERS);
BKE_library_foreach_ID_link(nullptr, id_iter, expand_cb, &expander, flag);
do_it = true;

View File

@ -852,6 +852,10 @@ void blo_do_versions_userdef(UserDef *userdef)
userdef->node_preview_res = 120;
}
if (!USER_VERSION_ATLEAST(400, 18)) {
userdef->playback_fps_samples = 8;
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -15,8 +15,10 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
shared ivec2 rect_min;
shared ivec2 rect_max;
shared int rect_min_x;
shared int rect_min_y;
shared int rect_max_x;
shared int rect_max_y;
shared int view_index;
/**
@ -68,8 +70,10 @@ void main()
/* Compute update area. */
if (all(equal(gl_LocalInvocationID, uvec3(0)))) {
rect_min = ivec2(SHADOW_TILEMAP_RES);
rect_max = ivec2(0);
rect_min_x = SHADOW_TILEMAP_RES;
rect_min_y = SHADOW_TILEMAP_RES;
rect_max_x = 0;
rect_max_y = 0;
view_index = -1;
}
@ -78,14 +82,17 @@ void main()
bool lod_valid_thread = all(equal(tile_co, tile_co_lod << lod));
bool do_page_render = tile.is_used && tile.do_update && lod_valid_thread;
if (do_page_render) {
atomicMin(rect_min.x, tile_co_lod.x);
atomicMin(rect_min.y, tile_co_lod.y);
atomicMax(rect_max.x, tile_co_lod.x + 1);
atomicMax(rect_max.y, tile_co_lod.y + 1);
atomicMin(rect_min_x, tile_co_lod.x);
atomicMin(rect_min_y, tile_co_lod.y);
atomicMax(rect_max_x, tile_co_lod.x + 1);
atomicMax(rect_max_y, tile_co_lod.y + 1);
}
barrier();
ivec2 rect_min = ivec2(rect_min_x, rect_min_y);
ivec2 rect_max = ivec2(rect_max_x, rect_max_y);
int viewport_index = viewport_select(rect_max - rect_min);
ivec2 viewport_size = viewport_size_get(viewport_index);

View File

@ -14,7 +14,7 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
shared bool directional_range_changed;
shared int directional_range_changed;
ShadowTileDataPacked init_tile_data(ShadowTileDataPacked tile, bool do_update)
{
@ -39,7 +39,7 @@ void main()
/* Reset shift to not tag for update more than once per sync cycle. */
tilemaps_buf[tilemap_index].grid_shift = ivec2(0);
directional_range_changed = false;
directional_range_changed = 0;
int clip_index = tilemap.clip_data_index;
if (clip_index == -1) {
@ -51,7 +51,7 @@ void main()
float clip_far_new = orderedIntBitsToFloat(clip_data.clip_far);
bool near_changed = clip_near_new != clip_data.clip_near_stored;
bool far_changed = clip_far_new != clip_data.clip_far_stored;
directional_range_changed = near_changed || far_changed;
directional_range_changed = int(near_changed || far_changed);
/* NOTE(fclem): This assumes clip near/far are computed each time the initial phase runs. */
tilemaps_clip_buf[clip_index].clip_near_stored = clip_near_new;
tilemaps_clip_buf[clip_index].clip_far_stored = clip_far_new;
@ -78,7 +78,7 @@ void main()
bool do_update = !in_range_inclusive(tile_shifted, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1));
/* TODO(fclem): Might be better to resize the depth stored instead of a full render update. */
if (directional_range_changed) {
if (directional_range_changed != 0) {
do_update = true;
}

View File

@ -2192,6 +2192,14 @@ static int animchannels_delete_exec(bContext *C, wmOperator * /*op*/)
ale->update = ANIM_UPDATE_DEPS;
break;
}
case ANIMTYPE_GREASE_PENCIL_LAYER: {
using namespace blender::bke::greasepencil;
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
Layer *layer = static_cast<Layer *>(ale->data);
grease_pencil->remove_layer(*layer);
DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
break;
}
case ANIMTYPE_MASKLAYER: {
/* Mask layer */
Mask *mask = (Mask *)ale->id;

View File

@ -43,20 +43,24 @@ enum {
/* ----------------------------------------------------- */
#define REDRAW_FRAME_AVERAGE 8
/**
* For playback frame-rate info stored during runtime as `scene->fps_info`.
*/
struct ScreenFrameRateInfo {
double redrawtime;
double lredrawtime;
float redrawtimes_fps[REDRAW_FRAME_AVERAGE];
short redrawtime_index;
/** The target FPS, use to reset on change. */
float fps_target;
/** Final result, ignore when -1.0. */
float fps_average;
int redrawtimes_index;
int redrawtimes_num;
int redrawtimes_num_set;
/** Over allocate. */
float redrawtimes_fps[0];
};
/* ----------------------------------------------------- */

View File

@ -294,15 +294,15 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (deltax != 0) {
RNA_int_set(op->ptr, "deltax", deltax);
vpd->lastx = event->xy[0];
}
if (deltay != 0) {
RNA_int_set(op->ptr, "deltay", deltay);
vpd->lasty = event->xy[1];
}
if (deltax || deltay) {
RNA_int_set(op->ptr, "deltax", deltax);
RNA_int_set(op->ptr, "deltay", deltay);
view_pan_apply(C, op);
}
break;

View File

@ -1694,22 +1694,30 @@ void ED_scene_fps_average_accumulate(Scene *scene, const double ltime)
/* Playback running. */
const float fps_target = float(FPS);
ScreenFrameRateInfo *fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
/* Reset when the target FPS changes.
* Needed redraw times from when a different FPS was set do not contribute
* to an average that is over/under the new target. */
if (fpsi && (fpsi->fps_target != fps_target)) {
MEM_freeN(fpsi);
fpsi = nullptr;
scene->fps_info = nullptr;
ScreenFrameRateInfo *fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
const int redrawtimes_num = U.playback_fps_samples ? U.playback_fps_samples :
max_ii(1, int(ceilf(fps_target)));
if (fpsi) {
/* Reset when the target FPS changes.
* Needed redraw times from when a different FPS was set do not contribute
* to an average that is over/under the new target. */
if ((fpsi->fps_target != fps_target) || (fpsi->redrawtimes_num != redrawtimes_num)) {
MEM_freeN(fpsi);
fpsi = nullptr;
scene->fps_info = nullptr;
}
}
/* If there isn't any info, initialize it first. */
if (fpsi == nullptr) {
fpsi = static_cast<ScreenFrameRateInfo *>(
scene->fps_info = MEM_callocN(sizeof(ScreenFrameRateInfo), __func__));
scene->fps_info = MEM_callocN(sizeof(ScreenFrameRateInfo) + (sizeof(float) * redrawtimes_num),
__func__);
fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
fpsi->fps_target = fps_target;
fpsi->redrawtimes_num = redrawtimes_num;
fpsi->redrawtimes_num_set = 0;
}
/* Update the values. */
@ -1738,22 +1746,23 @@ bool ED_scene_fps_average_calc(const Scene *scene)
return true;
}
fpsi->redrawtimes_fps[fpsi->redrawtime_index] = float(1.0 /
(fpsi->lredrawtime - fpsi->redrawtime));
if (fpsi->redrawtimes_index >= fpsi->redrawtimes_num) {
fpsi->redrawtimes_index = 0;
}
/* Doing an average for a more robust calculation. */
fpsi->redrawtimes_fps[fpsi->redrawtimes_index] = float(1.0 /
(fpsi->lredrawtime - fpsi->redrawtime));
fpsi->redrawtimes_index++;
if (fpsi->redrawtimes_index > fpsi->redrawtimes_num_set) {
fpsi->redrawtimes_num_set = fpsi->redrawtimes_index;
}
BLI_assert(fpsi->redrawtimes_num_set > 0);
float fps = 0.0f;
int tot = 0;
for (int i = 0; i < REDRAW_FRAME_AVERAGE; i++) {
if (fpsi->redrawtimes_fps[i]) {
fps += fpsi->redrawtimes_fps[i];
tot++;
}
for (int i = 0; i < fpsi->redrawtimes_num_set; i++) {
fps += fpsi->redrawtimes_fps[i];
}
if (tot) {
fpsi->redrawtime_index = (fpsi->redrawtime_index + 1) % REDRAW_FRAME_AVERAGE;
fps = fps / tot;
}
fpsi->fps_average = fps;
fpsi->fps_average = fps / float(fpsi->redrawtimes_num_set);
return true;
}

View File

@ -114,7 +114,7 @@ struct EraseOperationExecutor {
}
struct SegmentCircleIntersection {
/* Position of the intersection in the segment.*/
/* Position of the intersection in the segment. */
float factor = -1.0f;
/* True if the intersection corresponds to an inside/outside transition with respect to the

View File

@ -923,7 +923,10 @@ typedef struct UserDef {
/** #eGPUBackendType */
short gpu_backend;
char _pad7[4];
/** Number of samples for FPS display calculations. */
uchar playback_fps_samples;
char _pad7[3];
/** Private, defaults to 20 for 72 DPI setting. */
short widget_unit;

View File

@ -4833,6 +4833,16 @@ static void rna_def_userdef_view(BlenderRNA *brna)
"overlay while animation is played back");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "playback_fps_samples", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "playback_fps_samples");
RNA_def_property_range(prop, 0, 200);
RNA_def_property_ui_text(
prop,
"FPS Average Samples",
"The number of frames to use for calculating FPS average. "
"Zero to calculate this automatically, where the number of samples matches the target FPS");
RNA_def_property_update(prop, 0, "rna_userdef_update");
USERDEF_TAG_DIRTY_PROPERTY_UPDATE_DISABLE;
prop = RNA_def_property(srna, "show_addons_enabled_only", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(

View File

@ -49,6 +49,52 @@ class TestBlendFileSaveLoadBasic(TestHelper):
assert orig_data == read_data
class TestBlendFileSavePartial(TestHelper):
OBJECT_MESH_NAME = "ObjectMesh"
OBJECT_MATERIAL_NAME = "ObjectMaterial"
OBJECT_NAME = "Object"
UNUSED_MESH_NAME = "UnusedMesh"
def __init__(self, args):
self.args = args
def test_save_load(self):
bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
ob_mesh = bpy.data.meshes.new(self.OBJECT_MESH_NAME)
ob_material = bpy.data.materials.new(self.OBJECT_MATERIAL_NAME)
ob_mesh.materials.append(ob_material)
ob = bpy.data.objects.new(self.OBJECT_NAME, object_data=ob_mesh)
bpy.context.collection.objects.link(ob)
unused_mesh = bpy.data.meshes.new(self.UNUSED_MESH_NAME)
unused_mesh.materials.append(ob_material)
assert ob_mesh.users == 1
assert ob_material.users == 2
assert ob.users == 1
assert unused_mesh.users == 0
output_dir = self.args.output_dir
self.ensure_path(output_dir)
# Take care to keep the name unique so multiple test jobs can run at once.
output_path = os.path.join(output_dir, "blendfile_io_partial.blend")
bpy.data.libraries.write(filepath=output_path, datablocks={ob, unused_mesh}, fake_user=False)
bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False)
assert self.OBJECT_MESH_NAME in bpy.data.meshes
assert self.OBJECT_MATERIAL_NAME in bpy.data.materials
assert self.OBJECT_NAME in bpy.data.objects
assert self.UNUSED_MESH_NAME in bpy.data.meshes
assert bpy.data.meshes[self.OBJECT_MESH_NAME].users == 1
assert bpy.data.materials[self.OBJECT_MATERIAL_NAME].users == 2
assert bpy.data.objects[self.OBJECT_NAME].users == 0
assert bpy.data.meshes[self.UNUSED_MESH_NAME].users == 0
# NOTE: Technically this should rather be in `bl_id_management.py` test, but that file uses `unittest` module,
# which makes mixing it with tests system used here and passing extra parameters complicated.
# Since the main effect of 'RUNTIME' ID tag is on file save, it can as well be here for now.
@ -145,6 +191,7 @@ class TestIdRuntimeTag(TestHelper):
TESTS = (
TestBlendFileSaveLoadBasic,
TestBlendFileSavePartial,
TestIdRuntimeTag,
)