WIP: Functions: new local allocator for better memory reuse and performance #104630
|
@ -21,7 +21,7 @@ struct Global;
|
|||
/**
|
||||
* This is stored per thread. Align to cache line size to avoid false sharing.
|
||||
*/
|
||||
struct alignas(64) Local {
|
||||
struct alignas(128) Local {
|
||||
/**
|
||||
* Retain shared ownership of #Global to make sure that it is not destructed.
|
||||
*/
|
||||
|
|
|
@ -434,8 +434,10 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
|
|||
GMutableSpan{type, buffer, domain_size});
|
||||
evaluator.evaluate();
|
||||
|
||||
if (GAttributeWriter attribute = attributes.lookup_for_write(attribute_id)) {
|
||||
if (attribute.domain == domain && attribute.varray.type() == type) {
|
||||
const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(attribute_id);
|
||||
|
||||
if (meta_data && meta_data->domain == domain && meta_data->data_type == data_type) {
|
||||
if (GAttributeWriter attribute = attributes.lookup_for_write(attribute_id)) {
|
||||
attribute.varray.set_all(buffer);
|
||||
attribute.finish();
|
||||
type.destruct_n(buffer, domain_size);
|
||||
|
@ -443,6 +445,7 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
attributes.remove(attribute_id);
|
||||
if (attributes.add(attribute_id, domain, data_type, bke::AttributeInitMoveArray{buffer})) {
|
||||
return true;
|
||||
|
|
|
@ -66,9 +66,11 @@ typedef struct PoseBlendData {
|
|||
/* For temp-loading the Action from the pose library. */
|
||||
AssetTempIDConsumer *temp_id_consumer;
|
||||
|
||||
/* Blend factor, interval [-1, 1] for interpolating between current and given pose.
|
||||
* Positive factors will blend in `act`, whereas negative factors will blend in `act_flipped`. */
|
||||
/* Blend factor for interpolating between current and given pose.
|
||||
* 1.0 means "100% pose asset". Negative values and values > 1.0 will be used as-is, and can
|
||||
* cause interesting effects. */
|
||||
float blend_factor;
|
||||
bool is_flipped;
|
||||
struct PoseBackup *pose_backup;
|
||||
|
||||
Object *ob; /* Object to work on. */
|
||||
|
@ -85,11 +87,11 @@ typedef struct PoseBlendData {
|
|||
} PoseBlendData;
|
||||
|
||||
/** Return the bAction that should be blended.
|
||||
* This is either pbd->act or pbd->act_flipped, depending on the sign of the blend factor.
|
||||
* This is either pbd->act or pbd->act_flipped, depending on is_flipped.
|
||||
*/
|
||||
static bAction *poselib_action_to_blend(PoseBlendData *pbd)
|
||||
{
|
||||
return (pbd->blend_factor >= 0) ? pbd->act : pbd->act_flipped;
|
||||
return pbd->is_flipped ? pbd->act_flipped : pbd->act;
|
||||
}
|
||||
|
||||
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
|
||||
|
@ -177,27 +179,32 @@ static void poselib_blend_apply(bContext *C, wmOperator *op)
|
|||
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f);
|
||||
bAction *to_blend = poselib_action_to_blend(pbd);
|
||||
BKE_pose_apply_action_blend(pbd->ob, to_blend, &anim_eval_context, fabs(pbd->blend_factor));
|
||||
BKE_pose_apply_action_blend(pbd->ob, to_blend, &anim_eval_context, pbd->blend_factor);
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
|
||||
static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor)
|
||||
{
|
||||
const bool sign_changed = signf(new_factor) != signf(pbd->blend_factor);
|
||||
if (sign_changed) {
|
||||
/* The zero point was crossed, meaning that the pose will be flipped. This means the pose
|
||||
* backup has to change, as it only contains the bones for one side. */
|
||||
BKE_pose_backup_restore(pbd->pose_backup);
|
||||
BKE_pose_backup_free(pbd->pose_backup);
|
||||
}
|
||||
|
||||
pbd->blend_factor = new_factor;
|
||||
pbd->needs_redraw = true;
|
||||
}
|
||||
|
||||
if (sign_changed) {
|
||||
poselib_backup_posecopy(pbd);
|
||||
static void poselib_set_flipped(PoseBlendData *pbd, const bool new_flipped)
|
||||
{
|
||||
if (pbd->is_flipped == new_flipped) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The pose will toggle between flipped and normal. This means the pose
|
||||
* backup has to change, as it only contains the bones for one side. */
|
||||
BKE_pose_backup_restore(pbd->pose_backup);
|
||||
BKE_pose_backup_free(pbd->pose_backup);
|
||||
|
||||
pbd->is_flipped = new_flipped;
|
||||
pbd->needs_redraw = true;
|
||||
|
||||
poselib_backup_posecopy(pbd);
|
||||
}
|
||||
|
||||
/* Return operator return value. */
|
||||
|
@ -220,6 +227,9 @@ static int poselib_blend_handle_event(bContext *UNUSED(C), wmOperator *op, const
|
|||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Ctrl manages the 'flipped' state. */
|
||||
poselib_set_flipped(pbd, event->modifier & KM_CTRL);
|
||||
|
||||
/* only accept 'press' event, and ignore 'release', so that we don't get double actions */
|
||||
if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
|
@ -318,14 +328,12 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Passing `flipped=True` is the same as flipping the sign of the blend factor. */
|
||||
const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped");
|
||||
const float multiply_factor = apply_flipped ? -1.0f : 1.0f;
|
||||
pbd->blend_factor = multiply_factor * RNA_float_get(op->ptr, "blend_factor");
|
||||
pbd->is_flipped = RNA_boolean_get(op->ptr, "flipped");
|
||||
pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor");
|
||||
|
||||
/* Only construct the flipped pose if there is a chance it's actually needed. */
|
||||
const bool is_interactive = (event != NULL);
|
||||
if (is_interactive || pbd->blend_factor < 0) {
|
||||
if (is_interactive || pbd->is_flipped) {
|
||||
pbd->act_flipped = flip_pose(C, ob, pbd->act);
|
||||
}
|
||||
|
||||
|
@ -352,6 +360,7 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
|
|||
ED_slider_init(pbd->slider, event);
|
||||
ED_slider_factor_set(pbd->slider, pbd->blend_factor);
|
||||
ED_slider_allow_overshoot_set(pbd->slider, true);
|
||||
ED_slider_allow_increments_set(pbd->slider, false);
|
||||
ED_slider_is_bidirectional_set(pbd->slider, true);
|
||||
}
|
||||
|
||||
|
@ -394,8 +403,8 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op)
|
|||
poselib_keytag_pose(C, scene, pbd);
|
||||
|
||||
/* Ensure the redo panel has the actually-used value, instead of the initial value. */
|
||||
RNA_float_set(op->ptr, "blend_factor", fabs(pbd->blend_factor));
|
||||
RNA_boolean_set(op->ptr, "flipped", pbd->blend_factor < 0);
|
||||
RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor);
|
||||
RNA_boolean_set(op->ptr, "flipped", pbd->is_flipped);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -485,7 +494,11 @@ static int poselib_blend_modal(bContext *C, wmOperator *op, const wmEvent *event
|
|||
strcpy(tab_string, TIP_("[Tab] - Show blended pose"));
|
||||
}
|
||||
|
||||
BLI_snprintf(status_string, sizeof(status_string), "%s | %s", tab_string, slider_string);
|
||||
BLI_snprintf(status_string,
|
||||
sizeof(status_string),
|
||||
"%s | %s | [Ctrl] - Flip Pose",
|
||||
tab_string,
|
||||
slider_string);
|
||||
ED_workspace_status_text(C, status_string);
|
||||
|
||||
poselib_blend_apply(C, op);
|
||||
|
@ -572,16 +585,14 @@ void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
|
|||
FLT_MAX,
|
||||
"Blend Factor",
|
||||
"Amount that the pose is applied on top of the existing poses. A negative "
|
||||
"value will apply the pose flipped over the X-axis",
|
||||
"value will subtract the pose instead of adding it",
|
||||
-1.0f,
|
||||
1.0f);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna,
|
||||
"flipped",
|
||||
false,
|
||||
"Apply Flipped",
|
||||
"When enabled, applies the pose flipped over the X-axis. This is the same as "
|
||||
"passing a negative `blend_factor`");
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"flipped",
|
||||
false,
|
||||
"Apply Flipped",
|
||||
"When enabled, applies the pose flipped over the X-axis");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
|
@ -612,7 +623,7 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
|
|||
FLT_MAX,
|
||||
"Blend Factor",
|
||||
"Amount that the pose is applied on top of the existing poses. A "
|
||||
"negative value will apply the pose flipped over the X-axis",
|
||||
"negative value will subtract the pose instead of adding it",
|
||||
-1.0f,
|
||||
1.0f);
|
||||
/* Blending should always start at 0%, and not at whatever percentage was last used. This RNA
|
||||
|
@ -624,8 +635,7 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
|
|||
"flipped",
|
||||
false,
|
||||
"Apply Flipped",
|
||||
"When enabled, applies the pose flipped over the X-axis. This is the "
|
||||
"same as passing a negative `blend_factor`");
|
||||
"When enabled, applies the pose flipped over the X-axis");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* \ingroup edasset
|
||||
*/
|
||||
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <optional>
|
||||
|
@ -413,17 +414,24 @@ static int init_indexer_entries_from_value(FileIndexerEntries &indexer_entries,
|
|||
/**
|
||||
* \brief References the asset library directory.
|
||||
*
|
||||
* The #AssetLibraryIndex instance is used to keep track of unused file indices. When reading any
|
||||
* used indices are removed from the list and when reading is finished the unused
|
||||
* indices are removed.
|
||||
* The #AssetLibraryIndex instance collects file indices that are existing before the actual
|
||||
* reading/updating starts. This way, the reading/updating can tag pre-existing files as used when
|
||||
* they are still needed. Remaining ones (indices that are not tagged as used) can be removed once
|
||||
* reading finishes.
|
||||
*/
|
||||
struct AssetLibraryIndex {
|
||||
struct PreexistingFileIndexInfo {
|
||||
bool is_used = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tracks indices that haven't been used yet.
|
||||
* File indices that are existing already before reading/updating performs changes. The key is
|
||||
* the absolute path. The value can store information like if the index is known to be used.
|
||||
*
|
||||
* Contains absolute paths to the indices.
|
||||
* Note that when deleting a file index (#delete_index_file()), it's also removed from here,
|
||||
* since it doesn't exist and isn't relevant to keep track of anymore.
|
||||
*/
|
||||
Set<std::string> unused_file_indices;
|
||||
Map<std::string /*path*/, PreexistingFileIndexInfo> preexisting_file_indices;
|
||||
|
||||
/**
|
||||
* \brief Absolute path where the indices of `library` are stored.
|
||||
|
@ -485,9 +493,10 @@ struct AssetLibraryIndex {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize to keep track of unused file indices.
|
||||
* Check for pre-existing index files to be able to track what is still used and what can be
|
||||
* removed. See #AssetLibraryIndex::preexisting_file_indices.
|
||||
*/
|
||||
void init_unused_index_files()
|
||||
void collect_preexisting_file_indices()
|
||||
{
|
||||
const char *index_path = indices_base_path.c_str();
|
||||
if (!BLI_is_dir(index_path)) {
|
||||
|
@ -498,7 +507,7 @@ struct AssetLibraryIndex {
|
|||
for (int i = 0; i < dir_entries_num; i++) {
|
||||
struct direntry *entry = &dir_entries[i];
|
||||
if (BLI_str_endswith(entry->relname, ".index.json")) {
|
||||
unused_file_indices.add_as(std::string(entry->path));
|
||||
preexisting_file_indices.add_as(std::string(entry->path));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,17 +516,53 @@ struct AssetLibraryIndex {
|
|||
|
||||
void mark_as_used(const std::string &filename)
|
||||
{
|
||||
unused_file_indices.remove(filename);
|
||||
PreexistingFileIndexInfo *preexisting = preexisting_file_indices.lookup_ptr(filename);
|
||||
if (preexisting) {
|
||||
preexisting->is_used = true;
|
||||
}
|
||||
}
|
||||
|
||||
int remove_unused_index_files() const
|
||||
/**
|
||||
* Removes the file index from disk and #preexisting_file_indices (invalidating its iterators, so
|
||||
* don't call while iterating).
|
||||
* \return true if deletion was successful.
|
||||
*/
|
||||
bool delete_file_index(const std::string &filename)
|
||||
{
|
||||
if (BLI_delete(filename.c_str(), false, false) == 0) {
|
||||
preexisting_file_indices.remove(filename);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A bug was creating empty index files for a while (see D16665). Remove empty index files from
|
||||
* this period, so they are regenerated.
|
||||
*/
|
||||
/* Implemented further below. */
|
||||
int remove_broken_index_files();
|
||||
|
||||
int remove_unused_index_files()
|
||||
{
|
||||
int num_files_deleted = 0;
|
||||
for (const std::string &unused_index : unused_file_indices) {
|
||||
const char *file_path = unused_index.c_str();
|
||||
CLOG_INFO(&LOG, 2, "Remove unused index file [%s].", file_path);
|
||||
BLI_delete(file_path, false, false);
|
||||
num_files_deleted++;
|
||||
|
||||
Set<StringRef> files_to_remove;
|
||||
|
||||
for (auto preexisting_index : preexisting_file_indices.items()) {
|
||||
if (preexisting_index.value.is_used) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string &file_path = preexisting_index.key;
|
||||
CLOG_INFO(&LOG, 2, "Remove unused index file [%s].", file_path.c_str());
|
||||
files_to_remove.add(preexisting_index.key);
|
||||
}
|
||||
|
||||
for (StringRef file_to_remove : files_to_remove) {
|
||||
if (delete_file_index(file_to_remove)) {
|
||||
num_files_deleted++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_files_deleted;
|
||||
|
@ -622,8 +667,13 @@ class AssetIndexFile : public AbstractFile {
|
|||
const size_t MIN_FILE_SIZE_WITH_ENTRIES = 32;
|
||||
std::string filename;
|
||||
|
||||
AssetIndexFile(AssetLibraryIndex &library_index, StringRef index_file_path)
|
||||
: library_index(library_index), filename(index_file_path)
|
||||
{
|
||||
}
|
||||
|
||||
AssetIndexFile(AssetLibraryIndex &library_index, BlendFile &asset_filename)
|
||||
: library_index(library_index), filename(library_index.index_file_path(asset_filename))
|
||||
: AssetIndexFile(library_index, library_index.index_file_path(asset_filename))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -687,6 +737,56 @@ class AssetIndexFile : public AbstractFile {
|
|||
}
|
||||
};
|
||||
|
||||
/* TODO(Julian): remove this after a short while. Just necessary for people who've been using alpha
|
||||
* builds from a certain period. */
|
||||
int AssetLibraryIndex::remove_broken_index_files()
|
||||
{
|
||||
Set<StringRef> files_to_remove;
|
||||
|
||||
preexisting_file_indices.foreach_item(
|
||||
[&](const std::string &index_path, const PreexistingFileIndexInfo &) {
|
||||
AssetIndexFile index_file(*this, index_path);
|
||||
|
||||
/* Bug was causing empty index files, so non-empty ones can be skipped. */
|
||||
if (index_file.constains_entries()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Use the file modification time stamp to attempt to remove empty index files from a
|
||||
* certain period (when the bug was in there). Starting from a day before the bug was
|
||||
* introduced until a day after the fix should be enough to mitigate possible local time
|
||||
* zone issues. */
|
||||
|
||||
std::tm tm_from{};
|
||||
tm_from.tm_year = 2022 - 1900; /* 2022 */
|
||||
tm_from.tm_mon = 11 - 1; /* November */
|
||||
tm_from.tm_mday = 8; /* Day before bug was introduced. */
|
||||
std::tm tm_to{};
|
||||
tm_from.tm_year = 2022 - 1900; /* 2022 */
|
||||
tm_from.tm_mon = 12 - 1; /* December */
|
||||
tm_from.tm_mday = 3; /* Day after fix. */
|
||||
std::time_t timestamp_from = std::mktime(&tm_from);
|
||||
std::time_t timestamp_to = std::mktime(&tm_to);
|
||||
BLI_stat_t stat = {};
|
||||
if (BLI_stat(index_file.get_file_path(), &stat) == -1) {
|
||||
return;
|
||||
}
|
||||
if (IN_RANGE(stat.st_mtime, timestamp_from, timestamp_to)) {
|
||||
CLOG_INFO(&LOG, 2, "Remove potentially broken index file [%s].", index_path.c_str());
|
||||
files_to_remove.add(index_path);
|
||||
}
|
||||
});
|
||||
|
||||
int num_files_deleted = 0;
|
||||
for (StringRef files_to_remove : files_to_remove) {
|
||||
if (delete_file_index(files_to_remove)) {
|
||||
num_files_deleted++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_files_deleted;
|
||||
}
|
||||
|
||||
static eFileIndexerResult read_index(const char *filename,
|
||||
FileIndexerEntries *entries,
|
||||
int *r_read_entries_len,
|
||||
|
@ -762,7 +862,8 @@ static void *init_user_data(const char *root_directory, size_t root_directory_ma
|
|||
{
|
||||
AssetLibraryIndex *library_index = MEM_new<AssetLibraryIndex>(
|
||||
__func__, StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxlen)));
|
||||
library_index->init_unused_index_files();
|
||||
library_index->collect_preexisting_file_indices();
|
||||
library_index->remove_broken_index_files();
|
||||
return library_index;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,9 @@ void ED_slider_factor_set(struct tSlider *slider, float factor);
|
|||
bool ED_slider_allow_overshoot_get(struct tSlider *slider);
|
||||
void ED_slider_allow_overshoot_set(struct tSlider *slider, bool value);
|
||||
|
||||
bool ED_slider_allow_increments_get(struct tSlider *slider);
|
||||
void ED_slider_allow_increments_set(struct tSlider *slider, bool value);
|
||||
|
||||
bool ED_slider_is_bidirectional_get(struct tSlider *slider);
|
||||
void ED_slider_is_bidirectional_set(struct tSlider *slider, bool value);
|
||||
|
||||
|
|
|
@ -89,6 +89,10 @@ typedef struct tSlider {
|
|||
* This is set by the artist while using the slider. */
|
||||
bool overshoot;
|
||||
|
||||
/** Whether keeping CTRL pressed will snap to 10% increments.
|
||||
* Default is true. Set to false if the CTRL key is needed for other means. */
|
||||
bool allow_increments;
|
||||
|
||||
/** Move factor in 10% steps. */
|
||||
bool increments;
|
||||
|
||||
|
@ -381,6 +385,7 @@ tSlider *ED_slider_create(struct bContext *C)
|
|||
|
||||
/* Default is true, caller needs to manually set to false. */
|
||||
slider->allow_overshoot = true;
|
||||
slider->allow_increments = true;
|
||||
|
||||
/* Set initial factor. */
|
||||
slider->raw_factor = 0.5f;
|
||||
|
@ -425,7 +430,7 @@ bool ED_slider_modal(tSlider *slider, const wmEvent *event)
|
|||
break;
|
||||
case EVT_LEFTCTRLKEY:
|
||||
case EVT_RIGHTCTRLKEY:
|
||||
slider->increments = event->val == KM_PRESS;
|
||||
slider->increments = slider->allow_increments && event->val == KM_PRESS;
|
||||
break;
|
||||
case MOUSEMOVE:;
|
||||
/* Update factor. */
|
||||
|
@ -469,16 +474,21 @@ void ED_slider_status_string_get(const struct tSlider *slider,
|
|||
STRNCPY(precision_str, TIP_("Shift - Hold for precision"));
|
||||
}
|
||||
|
||||
if (slider->increments) {
|
||||
STRNCPY(increments_str, TIP_("[Ctrl] - Increments active"));
|
||||
if (slider->allow_increments) {
|
||||
if (slider->increments) {
|
||||
STRNCPY(increments_str, TIP_(" | [Ctrl] - Increments active"));
|
||||
}
|
||||
else {
|
||||
STRNCPY(increments_str, TIP_(" | Ctrl - Hold for 10% increments"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments"));
|
||||
increments_str[0] = '\0';
|
||||
}
|
||||
|
||||
BLI_snprintf(status_string,
|
||||
size_of_status_string,
|
||||
"%s | %s | %s",
|
||||
"%s | %s%s",
|
||||
overshoot_str,
|
||||
precision_str,
|
||||
increments_str);
|
||||
|
@ -521,6 +531,16 @@ void ED_slider_allow_overshoot_set(struct tSlider *slider, const bool value)
|
|||
slider->allow_overshoot = value;
|
||||
}
|
||||
|
||||
bool ED_slider_allow_increments_get(struct tSlider *slider)
|
||||
{
|
||||
return slider->allow_increments;
|
||||
}
|
||||
|
||||
void ED_slider_allow_increments_set(struct tSlider *slider, const bool value)
|
||||
{
|
||||
slider->allow_increments = value;
|
||||
}
|
||||
|
||||
bool ED_slider_is_bidirectional_get(struct tSlider *slider)
|
||||
{
|
||||
return slider->is_bidirectional;
|
||||
|
|
|
@ -241,7 +241,7 @@ class TestEnvironment:
|
|||
f'args = pickle.loads(base64.b64decode({args}))\n'
|
||||
f'result = {modulename}.{functionname}(args)\n'
|
||||
f'result = base64.b64encode(pickle.dumps(result))\n'
|
||||
f'print("{output_prefix}" + result.decode())\n')
|
||||
f'print("\\n{output_prefix}" + result.decode() + "\\n")\n')
|
||||
|
||||
expr_args = blender_args + ['--python-expr', expression]
|
||||
lines = self.call_blender(expr_args, foreground=foreground)
|
||||
|
|
|
@ -11,18 +11,33 @@ def _run(args):
|
|||
# Evaluate objects once first, to avoid any possible lazy evaluation later.
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
# Tag all objects with geometry nodes modifiers to be recalculated.
|
||||
for ob in bpy.context.view_layer.objects:
|
||||
for modifier in ob.modifiers:
|
||||
if modifier.type == 'NODES':
|
||||
ob.update_tag()
|
||||
break
|
||||
test_time_start = time.time()
|
||||
measured_times = []
|
||||
|
||||
start_time = time.time()
|
||||
bpy.context.view_layer.update()
|
||||
elapsed_time = time.time() - start_time
|
||||
min_measurements = 5
|
||||
max_measurements = 100
|
||||
timeout = 5
|
||||
|
||||
result = {'time': elapsed_time}
|
||||
while True:
|
||||
# Tag all objects with geometry nodes modifiers to be recalculated.
|
||||
for ob in bpy.context.view_layer.objects:
|
||||
for modifier in ob.modifiers:
|
||||
if modifier.type == 'NODES':
|
||||
ob.update_tag()
|
||||
break
|
||||
|
||||
start_time = time.time()
|
||||
bpy.context.view_layer.update()
|
||||
elapsed_time = time.time() - start_time
|
||||
measured_times.append(elapsed_time)
|
||||
|
||||
if len(measured_times) >= min_measurements and test_time_start + timeout < time.time():
|
||||
break
|
||||
if len(measured_times) >= max_measurements:
|
||||
break
|
||||
|
||||
average_time = sum(measured_times) / len(measured_times)
|
||||
result = {'time': average_time}
|
||||
return result
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue