Compare commits
22 Commits
temp-geome
...
temp-paral
Author | SHA1 | Date | |
---|---|---|---|
4469102925 | |||
0eb570cbbb | |||
e3594c8e65 | |||
bcae5507b8 | |||
bf47fb40fd | |||
2920a569b5 | |||
0f6be4e152 | |||
0c0e5a8420 | |||
f8ead736a0 | |||
9bb99532a5 | |||
b813648378 | |||
3da09f4e29 | |||
bda9e4238a | |||
c8f80453d5 | |||
f9ebd17b4b | |||
df65103bf0 | |||
a131e3bec7 | |||
4e91cd5c11 | |||
8f785524ae | |||
5bfc3a3421 | |||
6fc94d1848 | |||
068f012221 |
@@ -267,7 +267,8 @@ class OBJECT_PT_instancing(ObjectButtonsPanel, Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
return (ob.type in {'MESH', 'EMPTY', 'POINTCLOUD'})
|
||||
# FONT objects need (vertex) instancing for the 'Object Font' feature
|
||||
return (ob.type in {'MESH', 'EMPTY', 'POINTCLOUD', 'FONT'})
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
@@ -1659,7 +1659,7 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_split = False
|
||||
|
||||
st = context.space_data
|
||||
strip = context.active_sequence_strip
|
||||
@@ -1667,20 +1667,39 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel):
|
||||
|
||||
layout.active = not strip.mute
|
||||
|
||||
col = layout.column()
|
||||
|
||||
col.prop(strip, "volume", text="Volume")
|
||||
col.prop(strip, "pitch")
|
||||
|
||||
col = layout.column()
|
||||
col.prop(strip, "pan")
|
||||
col.enabled = sound is not None and sound.use_mono
|
||||
|
||||
if sound is not None:
|
||||
col = layout.column()
|
||||
|
||||
split = col.split(factor=0.4)
|
||||
split.label(text="")
|
||||
split.prop(sound, "use_mono")
|
||||
if st.waveform_display_type == 'DEFAULT_WAVEFORMS':
|
||||
col.prop(strip, "show_waveform")
|
||||
col.prop(sound, "use_mono")
|
||||
split = col.split(factor=0.4)
|
||||
split.label(text="")
|
||||
split.prop(strip, "show_waveform")
|
||||
|
||||
col = layout.column()
|
||||
|
||||
split = col.split(factor=0.4)
|
||||
split.alignment = 'RIGHT'
|
||||
split.label(text="Volume")
|
||||
split.prop(strip, "volume", text="")
|
||||
|
||||
split = col.split(factor=0.4)
|
||||
split.alignment = 'RIGHT'
|
||||
split.label(text="Pitch")
|
||||
split.prop(strip, "pitch", text="")
|
||||
|
||||
split = col.split(factor=0.4)
|
||||
split.alignment = 'RIGHT'
|
||||
split.label(text="Pan")
|
||||
audio_channels = context.scene.render.ffmpeg.audio_channels
|
||||
pan_text = ""
|
||||
if audio_channels != 'MONO' and audio_channels != 'STEREO':
|
||||
pan_text = "%.2f°" % (strip.pan * 90)
|
||||
split.prop(strip, "pan", text=pan_text)
|
||||
split.enabled = sound.use_mono and audio_channels != 'MONO'
|
||||
|
||||
|
||||
|
||||
class SEQUENCER_PT_adjust_comp(SequencerButtonsPanel, Panel):
|
||||
|
@@ -148,8 +148,8 @@ static void brush_make_local(Main *bmain, ID *id, const int flags)
|
||||
|
||||
Brush *brush = (Brush *)id;
|
||||
const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
|
||||
const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
|
||||
const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
|
||||
bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
|
||||
bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
|
||||
BLI_assert(force_copy == false || force_copy != force_local);
|
||||
|
||||
bool is_local = false, is_lib = false;
|
||||
@@ -166,27 +166,33 @@ static void brush_make_local(Main *bmain, ID *id, const int flags)
|
||||
|
||||
if (!force_local && !force_copy) {
|
||||
BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib);
|
||||
if (lib_local || is_local) {
|
||||
if (!is_lib) {
|
||||
force_local = true;
|
||||
}
|
||||
else {
|
||||
force_copy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lib_local || is_local || force_copy || force_local) {
|
||||
if (!is_lib || force_local) {
|
||||
BKE_lib_id_clear_library_data(bmain, &brush->id);
|
||||
BKE_lib_id_expand_local(bmain, &brush->id);
|
||||
if (force_local) {
|
||||
BKE_lib_id_clear_library_data(bmain, &brush->id);
|
||||
BKE_lib_id_expand_local(bmain, &brush->id);
|
||||
|
||||
/* enable fake user by default */
|
||||
id_fake_user_set(&brush->id);
|
||||
}
|
||||
else {
|
||||
Brush *brush_new = (Brush *)BKE_id_copy(bmain, &brush->id); /* Ensures FAKE_USER is set */
|
||||
/* enable fake user by default */
|
||||
id_fake_user_set(&brush->id);
|
||||
}
|
||||
else if (force_copy) {
|
||||
Brush *brush_new = (Brush *)BKE_id_copy(bmain, &brush->id); /* Ensures FAKE_USER is set */
|
||||
|
||||
brush_new->id.us = 0;
|
||||
brush_new->id.us = 0;
|
||||
|
||||
/* setting newid is mandatory for complex make_lib_local logic... */
|
||||
ID_NEW_SET(brush, brush_new);
|
||||
/* setting newid is mandatory for complex make_lib_local logic... */
|
||||
ID_NEW_SET(brush, brush_new);
|
||||
|
||||
if (!lib_local) {
|
||||
BKE_libblock_remap(bmain, brush, brush_new, ID_REMAP_SKIP_INDIRECT_USAGE);
|
||||
}
|
||||
if (!lib_local) {
|
||||
BKE_libblock_remap(bmain, brush, brush_new, ID_REMAP_SKIP_INDIRECT_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -411,8 +411,8 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags)
|
||||
}
|
||||
|
||||
const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
|
||||
const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
|
||||
const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
|
||||
bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
|
||||
bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
|
||||
BLI_assert(force_copy == false || force_copy != force_local);
|
||||
|
||||
bool is_local = false, is_lib = false;
|
||||
@@ -426,42 +426,48 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags)
|
||||
|
||||
if (!force_copy && !force_local) {
|
||||
BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib);
|
||||
if (lib_local || is_local) {
|
||||
if (!is_lib) {
|
||||
force_local = true;
|
||||
}
|
||||
else {
|
||||
force_copy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lib_local || is_local || force_copy || force_local) {
|
||||
if (!is_lib || force_local) {
|
||||
BKE_lib_id_clear_library_data(bmain, id);
|
||||
BKE_lib_id_expand_local(bmain, id);
|
||||
}
|
||||
else {
|
||||
ID *id_new = BKE_id_copy(bmain, id);
|
||||
if (force_local) {
|
||||
BKE_lib_id_clear_library_data(bmain, id);
|
||||
BKE_lib_id_expand_local(bmain, id);
|
||||
}
|
||||
else if (force_copy) {
|
||||
ID *id_new = BKE_id_copy(bmain, id);
|
||||
|
||||
/* Should not fail in expected use cases,
|
||||
* but a few ID types cannot be copied (LIB, WM, SCR...). */
|
||||
if (id_new != NULL) {
|
||||
id_new->us = 0;
|
||||
/* Should not fail in expected use cases,
|
||||
* but a few ID types cannot be copied (LIB, WM, SCR...). */
|
||||
if (id_new != NULL) {
|
||||
id_new->us = 0;
|
||||
|
||||
/* setting newid is mandatory for complex make_lib_local logic... */
|
||||
ID_NEW_SET(id, id_new);
|
||||
Key *key = BKE_key_from_id(id), *key_new = BKE_key_from_id(id);
|
||||
if (key && key_new) {
|
||||
ID_NEW_SET(key, key_new);
|
||||
}
|
||||
bNodeTree *ntree = ntreeFromID(id), *ntree_new = ntreeFromID(id_new);
|
||||
if (ntree && ntree_new) {
|
||||
ID_NEW_SET(ntree, ntree_new);
|
||||
}
|
||||
if (GS(id->name) == ID_SCE) {
|
||||
Collection *master_collection = ((Scene *)id)->master_collection,
|
||||
*master_collection_new = ((Scene *)id_new)->master_collection;
|
||||
if (master_collection && master_collection_new) {
|
||||
ID_NEW_SET(master_collection, master_collection_new);
|
||||
}
|
||||
/* setting newid is mandatory for complex make_lib_local logic... */
|
||||
ID_NEW_SET(id, id_new);
|
||||
Key *key = BKE_key_from_id(id), *key_new = BKE_key_from_id(id);
|
||||
if (key && key_new) {
|
||||
ID_NEW_SET(key, key_new);
|
||||
}
|
||||
bNodeTree *ntree = ntreeFromID(id), *ntree_new = ntreeFromID(id_new);
|
||||
if (ntree && ntree_new) {
|
||||
ID_NEW_SET(ntree, ntree_new);
|
||||
}
|
||||
if (GS(id->name) == ID_SCE) {
|
||||
Collection *master_collection = ((Scene *)id)->master_collection,
|
||||
*master_collection_new = ((Scene *)id_new)->master_collection;
|
||||
if (master_collection && master_collection_new) {
|
||||
ID_NEW_SET(master_collection, master_collection_new);
|
||||
}
|
||||
}
|
||||
|
||||
if (!lib_local) {
|
||||
BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE);
|
||||
}
|
||||
if (!lib_local) {
|
||||
BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -331,8 +331,8 @@ static void object_make_local(Main *bmain, ID *id, const int flags)
|
||||
Object *ob = (Object *)id;
|
||||
const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
|
||||
const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0;
|
||||
const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
|
||||
const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
|
||||
bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0;
|
||||
bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0;
|
||||
BLI_assert(force_copy == false || force_copy != force_local);
|
||||
|
||||
bool is_local = false, is_lib = false;
|
||||
@@ -346,32 +346,38 @@ static void object_make_local(Main *bmain, ID *id, const int flags)
|
||||
|
||||
if (!force_local && !force_copy) {
|
||||
BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib);
|
||||
}
|
||||
|
||||
if (lib_local || is_local || force_copy || force_local) {
|
||||
if (!is_lib || force_local) {
|
||||
BKE_lib_id_clear_library_data(bmain, &ob->id);
|
||||
BKE_lib_id_expand_local(bmain, &ob->id);
|
||||
if (clear_proxy) {
|
||||
if (ob->proxy_from != NULL) {
|
||||
ob->proxy_from->proxy = NULL;
|
||||
ob->proxy_from->proxy_group = NULL;
|
||||
}
|
||||
ob->proxy = ob->proxy_from = ob->proxy_group = NULL;
|
||||
if (lib_local || is_local) {
|
||||
if (!is_lib) {
|
||||
force_local = true;
|
||||
}
|
||||
else {
|
||||
force_copy = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id);
|
||||
id_us_min(&ob_new->id);
|
||||
}
|
||||
|
||||
ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = NULL;
|
||||
|
||||
/* setting newid is mandatory for complex make_lib_local logic... */
|
||||
ID_NEW_SET(ob, ob_new);
|
||||
|
||||
if (!lib_local) {
|
||||
BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE);
|
||||
if (force_local) {
|
||||
BKE_lib_id_clear_library_data(bmain, &ob->id);
|
||||
BKE_lib_id_expand_local(bmain, &ob->id);
|
||||
if (clear_proxy) {
|
||||
if (ob->proxy_from != NULL) {
|
||||
ob->proxy_from->proxy = NULL;
|
||||
ob->proxy_from->proxy_group = NULL;
|
||||
}
|
||||
ob->proxy = ob->proxy_from = ob->proxy_group = NULL;
|
||||
}
|
||||
}
|
||||
else if (force_copy) {
|
||||
Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id);
|
||||
id_us_min(&ob_new->id);
|
||||
|
||||
ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = NULL;
|
||||
|
||||
/* setting newid is mandatory for complex make_lib_local logic... */
|
||||
ID_NEW_SET(ob, ob_new);
|
||||
|
||||
if (!lib_local) {
|
||||
BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5312,7 +5318,7 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot)
|
||||
unsigned int i;
|
||||
|
||||
Mesh *me_eval = ob->runtime.mesh_deform_eval ? ob->runtime.mesh_deform_eval :
|
||||
ob->runtime.mesh_deform_eval;
|
||||
BKE_object_get_evaluated_mesh(ob);
|
||||
const int *index;
|
||||
|
||||
if (me_eval && (index = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX))) {
|
||||
|
@@ -653,7 +653,8 @@ inline void devirtualize_varray2(const VArray<T1> &varray1,
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* This fallback is used even when one of the inputs could be optimized. It's probably not worth
|
||||
/* This fallback is used even when one of the inputs could be optimized. It's probably not
|
||||
worth
|
||||
* it to optimize just one of the inputs, because then the compiler still has to call into
|
||||
* unknown code, which inhibits many compiler optimizations. */
|
||||
func(varray1, varray2);
|
||||
|
@@ -4500,7 +4500,8 @@ static void add_loose_objects_to_scene(Main *mainvar,
|
||||
* or for a collection when *lib has been set. */
|
||||
LISTBASE_FOREACH (Object *, ob, &mainvar->objects) {
|
||||
bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0;
|
||||
if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) {
|
||||
if (do_it ||
|
||||
((ob->id.tag & LIB_TAG_INDIRECT) != 0 && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) {
|
||||
if (do_append) {
|
||||
if (ob->id.us == 0) {
|
||||
do_it = true;
|
||||
@@ -4648,7 +4649,7 @@ static void add_collections_to_scene(Main *mainvar,
|
||||
LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) {
|
||||
Object *ob = coll_ob->ob;
|
||||
if ((ob->id.tag & (LIB_TAG_PRE_EXISTING | LIB_TAG_DOIT | LIB_TAG_INDIRECT)) == 0 &&
|
||||
(ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) {
|
||||
(ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == false)) {
|
||||
do_add_collection = true;
|
||||
break;
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ set(INC
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
../../../../intern/clog
|
||||
../../../../intern/eigen
|
||||
../../../../intern/glew-mx
|
||||
../../../../intern/guardedalloc
|
||||
|
@@ -39,9 +39,13 @@
|
||||
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
/* own includes */
|
||||
#include "gizmo_library_intern.h"
|
||||
|
||||
static CLG_LogRef LOG = {"ed.gizmo.library_utils"};
|
||||
|
||||
/* factor for precision tweaking */
|
||||
#define GIZMO_PRECISION_FAC 0.05f
|
||||
|
||||
@@ -182,7 +186,7 @@ bool gizmo_window_project_2d(bContext *C,
|
||||
bool use_offset,
|
||||
float r_co[2])
|
||||
{
|
||||
float mat[4][4];
|
||||
float mat[4][4], imat[4][4];
|
||||
{
|
||||
float mat_identity[4][4];
|
||||
struct WM_GizmoMatrixParams params = {NULL};
|
||||
@@ -193,6 +197,14 @@ bool gizmo_window_project_2d(bContext *C,
|
||||
WM_gizmo_calc_matrix_final_params(gz, ¶ms, mat);
|
||||
}
|
||||
|
||||
if (!invert_m4_m4(imat, mat)) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Gizmo \"%s\" of group \"%s\" has matrix that could not be inverted "
|
||||
"(projection will fail)",
|
||||
gz->type->idname,
|
||||
gz->parent_gzgroup->type->idname);
|
||||
}
|
||||
|
||||
/* rotate mouse in relation to the center and relocate it */
|
||||
if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
|
||||
/* For 3d views, transform 2D mouse pos onto plane. */
|
||||
@@ -202,8 +214,6 @@ bool gizmo_window_project_2d(bContext *C,
|
||||
plane_from_point_normal_v3(plane, mat[3], mat[2]);
|
||||
bool clip_ray = ((RegionView3D *)region->regiondata)->is_persp;
|
||||
if (ED_view3d_win_to_3d_on_plane(region, plane, mval, clip_ray, co)) {
|
||||
float imat[4][4];
|
||||
invert_m4_m4(imat, mat);
|
||||
mul_m4_v3(imat, co);
|
||||
r_co[0] = co[(axis + 1) % 3];
|
||||
r_co[1] = co[(axis + 2) % 3];
|
||||
@@ -213,8 +223,6 @@ bool gizmo_window_project_2d(bContext *C,
|
||||
}
|
||||
|
||||
float co[3] = {mval[0], mval[1], 0.0f};
|
||||
float imat[4][4];
|
||||
invert_m4_m4(imat, mat);
|
||||
mul_m4_v3(imat, co);
|
||||
copy_v2_v2(r_co, co);
|
||||
return true;
|
||||
@@ -223,7 +231,7 @@ bool gizmo_window_project_2d(bContext *C,
|
||||
bool gizmo_window_project_3d(
|
||||
bContext *C, const struct wmGizmo *gz, const float mval[2], bool use_offset, float r_co[3])
|
||||
{
|
||||
float mat[4][4];
|
||||
float mat[4][4], imat[4][4];
|
||||
{
|
||||
float mat_identity[4][4];
|
||||
struct WM_GizmoMatrixParams params = {NULL};
|
||||
@@ -234,20 +242,25 @@ bool gizmo_window_project_3d(
|
||||
WM_gizmo_calc_matrix_final_params(gz, ¶ms, mat);
|
||||
}
|
||||
|
||||
if (!invert_m4_m4(imat, mat)) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Gizmo \"%s\" of group \"%s\" has matrix that could not be inverted "
|
||||
"(projection will fail)",
|
||||
gz->type->idname,
|
||||
gz->parent_gzgroup->type->idname);
|
||||
}
|
||||
|
||||
if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
|
||||
View3D *v3d = CTX_wm_view3d(C);
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
/* NOTE: we might want a custom reference point passed in,
|
||||
* instead of the gizmo center. */
|
||||
ED_view3d_win_to_3d(v3d, region, mat[3], mval, r_co);
|
||||
invert_m4(mat);
|
||||
mul_m4_v3(mat, r_co);
|
||||
mul_m4_v3(imat, r_co);
|
||||
return true;
|
||||
}
|
||||
|
||||
float co[3] = {mval[0], mval[1], 0.0f};
|
||||
float imat[4][4];
|
||||
invert_m4_m4(imat, mat);
|
||||
mul_m4_v3(imat, co);
|
||||
copy_v2_v2(r_co, co);
|
||||
return true;
|
||||
|
@@ -137,7 +137,6 @@ void ED_view3d_smooth_view_ex(
|
||||
{
|
||||
RegionView3D *rv3d = region->regiondata;
|
||||
struct SmoothView3DStore sms = {{0}};
|
||||
bool ok = false;
|
||||
|
||||
/* initialize sms */
|
||||
view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d);
|
||||
@@ -200,29 +199,30 @@ void ED_view3d_smooth_view_ex(
|
||||
sms.to_camera = true; /* restore view3d values in end */
|
||||
}
|
||||
|
||||
/* skip smooth viewing for external render engine draw */
|
||||
if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) {
|
||||
bool changed = false; /* zero means no difference */
|
||||
bool changed = false; /* zero means no difference */
|
||||
|
||||
if (sview->camera_old != sview->camera) {
|
||||
changed = true;
|
||||
}
|
||||
else if (sms.dst.dist != rv3d->dist) {
|
||||
changed = true;
|
||||
}
|
||||
else if (sms.dst.lens != v3d->lens) {
|
||||
changed = true;
|
||||
}
|
||||
else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs)) {
|
||||
changed = true;
|
||||
}
|
||||
else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat)) {
|
||||
changed = true;
|
||||
}
|
||||
if (sview->camera_old != sview->camera) {
|
||||
changed = true;
|
||||
}
|
||||
else if (sms.dst.dist != rv3d->dist) {
|
||||
changed = true;
|
||||
}
|
||||
else if (sms.dst.lens != v3d->lens) {
|
||||
changed = true;
|
||||
}
|
||||
else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs)) {
|
||||
changed = true;
|
||||
}
|
||||
else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat)) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/* The new view is different from the previous state. */
|
||||
if (changed) {
|
||||
|
||||
/* Skip smooth viewing for external render engine draw. */
|
||||
if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) {
|
||||
|
||||
/* The new view is different from the old one
|
||||
* so animate the view */
|
||||
if (changed) {
|
||||
/* original values */
|
||||
if (sview->camera_old) {
|
||||
Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old);
|
||||
@@ -279,27 +279,25 @@ void ED_view3d_smooth_view_ex(
|
||||
}
|
||||
/* #TIMER1 is hard-coded in key-map. */
|
||||
rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0);
|
||||
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sms.to_camera == false) {
|
||||
copy_v3_v3(rv3d->ofs, sms.dst.ofs);
|
||||
copy_qt_qt(rv3d->viewquat, sms.dst.quat);
|
||||
rv3d->dist = sms.dst.dist;
|
||||
v3d->lens = sms.dst.lens;
|
||||
|
||||
/* if we get here nothing happens */
|
||||
if (ok == false) {
|
||||
if (sms.to_camera == false) {
|
||||
copy_v3_v3(rv3d->ofs, sms.dst.ofs);
|
||||
copy_qt_qt(rv3d->viewquat, sms.dst.quat);
|
||||
rv3d->dist = sms.dst.dist;
|
||||
v3d->lens = sms.dst.lens;
|
||||
ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
|
||||
}
|
||||
|
||||
ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
|
||||
if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
|
||||
view3d_boxview_copy(area, region);
|
||||
}
|
||||
|
||||
ED_region_tag_redraw(region);
|
||||
|
||||
WM_event_add_mousemove(win);
|
||||
}
|
||||
|
||||
if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
|
||||
view3d_boxview_copy(area, region);
|
||||
}
|
||||
|
||||
ED_region_tag_redraw(region);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,6 +318,7 @@ void ED_view3d_smooth_view(bContext *C,
|
||||
/* only meant for timer usage */
|
||||
static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
RegionView3D *rv3d = region->regiondata;
|
||||
struct SmoothView3DStore *sms = rv3d->sms;
|
||||
float step, step_inv;
|
||||
@@ -333,6 +332,7 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b
|
||||
|
||||
/* end timer */
|
||||
if (step >= 1.0f) {
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
/* if we went to camera, store the original */
|
||||
if (sms->to_camera) {
|
||||
@@ -355,9 +355,12 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b
|
||||
MEM_freeN(rv3d->sms);
|
||||
rv3d->sms = NULL;
|
||||
|
||||
WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer);
|
||||
WM_event_remove_timer(wm, win, rv3d->smooth_timer);
|
||||
rv3d->smooth_timer = NULL;
|
||||
rv3d->rflag &= ~RV3D_NAVIGATING;
|
||||
|
||||
/* Event handling won't know if a UI item has been moved under the pointer. */
|
||||
WM_event_add_mousemove(win);
|
||||
}
|
||||
else {
|
||||
/* ease in/out */
|
||||
@@ -380,12 +383,9 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b
|
||||
|
||||
const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
|
||||
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
|
||||
if (ED_screen_animation_playing(wm)) {
|
||||
ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
|
||||
}
|
||||
|
||||
/* Event handling won't know if a UI item has been moved under the pointer. */
|
||||
WM_event_add_mousemove(CTX_wm_window(C));
|
||||
}
|
||||
|
||||
if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) {
|
||||
|
@@ -34,9 +34,11 @@ set(SRC
|
||||
intern/generic_virtual_vector_array.cc
|
||||
intern/multi_function.cc
|
||||
intern/multi_function_builder.cc
|
||||
intern/multi_function_parallel.cc
|
||||
intern/multi_function_procedure.cc
|
||||
intern/multi_function_procedure_builder.cc
|
||||
intern/multi_function_procedure_executor.cc
|
||||
intern/multi_function_procedure_optimization.cc
|
||||
|
||||
FN_cpp_type.hh
|
||||
FN_cpp_type_make.hh
|
||||
@@ -54,9 +56,11 @@ set(SRC
|
||||
FN_multi_function_data_type.hh
|
||||
FN_multi_function_param_type.hh
|
||||
FN_multi_function_params.hh
|
||||
FN_multi_function_parallel.hh
|
||||
FN_multi_function_procedure.hh
|
||||
FN_multi_function_procedure_builder.hh
|
||||
FN_multi_function_procedure_executor.hh
|
||||
FN_multi_function_procedure_optimization.hh
|
||||
FN_multi_function_signature.hh
|
||||
)
|
||||
|
||||
@@ -64,6 +68,22 @@ set(LIB
|
||||
bf_blenlib
|
||||
)
|
||||
|
||||
if(WITH_TBB)
|
||||
add_definitions(-DWITH_TBB)
|
||||
if(WIN32)
|
||||
# TBB includes Windows.h which will define min/max macros
|
||||
# that will collide with the stl versions.
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
list(APPEND INC_SYS
|
||||
${TBB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
list(APPEND LIB
|
||||
${TBB_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
if(WITH_GTESTS)
|
||||
|
@@ -911,4 +911,50 @@ template<typename T> class GVMutableArray_Typed {
|
||||
}
|
||||
};
|
||||
|
||||
class GVArray_For_SlicedGVArray : public GVArray {
|
||||
protected:
|
||||
const GVArray &varray_;
|
||||
int64_t offset_;
|
||||
|
||||
public:
|
||||
GVArray_For_SlicedGVArray(const GVArray &varray, const IndexRange slice)
|
||||
: GVArray(varray.type(), slice.size()), varray_(varray), offset_(slice.start())
|
||||
{
|
||||
BLI_assert(slice.one_after_last() <= varray.size());
|
||||
}
|
||||
|
||||
void get_impl(const int64_t index, void *r_value) const override;
|
||||
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class to create the "best" sliced virtual array.
|
||||
*/
|
||||
class GVArray_Slice {
|
||||
private:
|
||||
const GVArray *varray_;
|
||||
/* Of these optional virtual arrays, at most one is constructed at any time. */
|
||||
std::optional<GVArray_For_GSpan> varray_span_;
|
||||
std::optional<GVArray_For_SingleValue> varray_single_;
|
||||
std::optional<GVArray_For_SlicedGVArray> varray_any_;
|
||||
|
||||
public:
|
||||
GVArray_Slice(const GVArray &varray, const IndexRange slice);
|
||||
|
||||
const GVArray &operator*()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
|
||||
const GVArray *operator->()
|
||||
{
|
||||
return varray_;
|
||||
}
|
||||
|
||||
operator const GVArray &()
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::fn
|
||||
|
39
source/blender/functions/FN_multi_function_parallel.hh
Normal file
39
source/blender/functions/FN_multi_function_parallel.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup fn
|
||||
*/
|
||||
|
||||
#include "FN_multi_function.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
class ParallelMultiFunction : public MultiFunction {
|
||||
private:
|
||||
const MultiFunction &fn_;
|
||||
const int64_t grain_size_;
|
||||
bool threading_supported_;
|
||||
|
||||
public:
|
||||
ParallelMultiFunction(const MultiFunction &fn, const int64_t grain_size);
|
||||
|
||||
void call(IndexMask mask, MFParams params, MFContext context) const override;
|
||||
};
|
||||
|
||||
} // namespace blender::fn
|
@@ -42,6 +42,55 @@ enum class MFInstructionType {
|
||||
Return,
|
||||
};
|
||||
|
||||
/**
|
||||
* An #MFInstructionCursor points to a position in a multi-function procedure, where an instruction
|
||||
* can be inserted.
|
||||
*/
|
||||
class MFInstructionCursor {
|
||||
public:
|
||||
enum Type {
|
||||
None,
|
||||
Entry,
|
||||
Call,
|
||||
Destruct,
|
||||
Branch,
|
||||
Dummy,
|
||||
};
|
||||
|
||||
private:
|
||||
Type type_ = None;
|
||||
MFInstruction *instruction_ = nullptr;
|
||||
/* Only used when it is a branch instruction. */
|
||||
bool branch_output_ = false;
|
||||
|
||||
public:
|
||||
MFInstructionCursor() = default;
|
||||
MFInstructionCursor(MFCallInstruction &instruction);
|
||||
MFInstructionCursor(MFDestructInstruction &instruction);
|
||||
MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output);
|
||||
MFInstructionCursor(MFDummyInstruction &instruction);
|
||||
|
||||
static MFInstructionCursor ForEntry();
|
||||
|
||||
MFInstruction *next(MFProcedure &procedure) const;
|
||||
void set_next(MFProcedure &procedure, MFInstruction *new_instruction) const;
|
||||
|
||||
MFInstruction *instruction() const;
|
||||
|
||||
Type type() const;
|
||||
|
||||
friend bool operator==(const MFInstructionCursor &a, const MFInstructionCursor &b)
|
||||
{
|
||||
return a.type_ == b.type_ && a.instruction_ == b.instruction_ &&
|
||||
a.branch_output_ == b.branch_output_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const MFInstructionCursor &a, const MFInstructionCursor &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A variable is similar to a virtual register in other libraries. During evaluation, every is
|
||||
* either uninitialized or contains a value for every index (remember, a multi-function procedure
|
||||
@@ -73,7 +122,7 @@ class MFVariable : NonCopyable, NonMovable {
|
||||
class MFInstruction : NonCopyable, NonMovable {
|
||||
protected:
|
||||
MFInstructionType type_;
|
||||
Vector<MFInstruction *> prev_;
|
||||
Vector<MFInstructionCursor> prev_;
|
||||
|
||||
friend MFProcedure;
|
||||
friend MFCallInstruction;
|
||||
@@ -89,8 +138,7 @@ class MFInstruction : NonCopyable, NonMovable {
|
||||
* Other instructions that come before this instruction. There can be multiple previous
|
||||
* instructions when branching is used in the procedure.
|
||||
*/
|
||||
Span<MFInstruction *> prev();
|
||||
Span<const MFInstruction *> prev() const;
|
||||
Span<MFInstructionCursor> prev() const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -246,6 +294,9 @@ class MFProcedure : NonCopyable, NonMovable {
|
||||
Span<MFVariable *> variables();
|
||||
Span<const MFVariable *> variables() const;
|
||||
|
||||
Span<MFDestructInstruction *> destruct_instructions();
|
||||
Span<const MFDestructInstruction *> destruct_instructions() const;
|
||||
|
||||
std::string to_dot() const;
|
||||
|
||||
bool validate() const;
|
||||
@@ -275,6 +326,50 @@ using MFDestructInstruction = fn::MFDestructInstruction;
|
||||
using MFProcedure = fn::MFProcedure;
|
||||
} // namespace multi_function_procedure_types
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFInstructionCursor inline methods.
|
||||
*/
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction)
|
||||
: type_(Call), instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction)
|
||||
: type_(Destruct), instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction,
|
||||
bool branch_output)
|
||||
: type_(Branch), instruction_(&instruction), branch_output_(branch_output)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction)
|
||||
: type_(Dummy), instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor MFInstructionCursor::ForEntry()
|
||||
{
|
||||
MFInstructionCursor cursor;
|
||||
cursor.type_ = Type::Entry;
|
||||
return cursor;
|
||||
}
|
||||
|
||||
inline MFInstruction *MFInstructionCursor::instruction() const
|
||||
{
|
||||
/* This isn't really const correct unfortunately, because to make it correct we'll need a const
|
||||
* version of #MFInstructionCursor. */
|
||||
return instruction_;
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::Type MFInstructionCursor::type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFVariable inline methods.
|
||||
*/
|
||||
@@ -308,12 +403,7 @@ inline MFInstructionType MFInstruction::type() const
|
||||
return type_;
|
||||
}
|
||||
|
||||
inline Span<MFInstruction *> MFInstruction::prev()
|
||||
{
|
||||
return prev_;
|
||||
}
|
||||
|
||||
inline Span<const MFInstruction *> MFInstruction::prev() const
|
||||
inline Span<MFInstructionCursor> MFInstruction::prev() const
|
||||
{
|
||||
return prev_;
|
||||
}
|
||||
@@ -449,4 +539,14 @@ inline Span<const MFVariable *> MFProcedure::variables() const
|
||||
return variables_;
|
||||
}
|
||||
|
||||
inline Span<MFDestructInstruction *> MFProcedure::destruct_instructions()
|
||||
{
|
||||
return destruct_instructions_;
|
||||
}
|
||||
|
||||
inline Span<const MFDestructInstruction *> MFProcedure::destruct_instructions() const
|
||||
{
|
||||
return destruct_instructions_;
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
||||
|
@@ -24,31 +24,6 @@
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
/**
|
||||
* An #MFInstructionCursor points to a position in a multi-function procedure, where an instruction
|
||||
* can be inserted.
|
||||
*/
|
||||
class MFInstructionCursor {
|
||||
private:
|
||||
MFInstruction *instruction_ = nullptr;
|
||||
/* Only used when it is a branch instruction. */
|
||||
bool branch_output_ = false;
|
||||
/* Only used when instruction is null. */
|
||||
bool is_entry_ = false;
|
||||
|
||||
public:
|
||||
MFInstructionCursor() = default;
|
||||
|
||||
MFInstructionCursor(MFCallInstruction &instruction);
|
||||
MFInstructionCursor(MFDestructInstruction &instruction);
|
||||
MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output);
|
||||
MFInstructionCursor(MFDummyInstruction &instruction);
|
||||
|
||||
static MFInstructionCursor Entry();
|
||||
|
||||
void insert(MFProcedure &procedure, MFInstruction *new_instruction);
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class to build a #MFProcedure.
|
||||
*/
|
||||
@@ -64,7 +39,7 @@ class MFProcedureBuilder {
|
||||
struct Loop;
|
||||
|
||||
MFProcedureBuilder(MFProcedure &procedure,
|
||||
MFInstructionCursor initial_cursor = MFInstructionCursor::Entry());
|
||||
MFInstructionCursor initial_cursor = MFInstructionCursor::ForEntry());
|
||||
|
||||
MFProcedureBuilder(Span<MFProcedureBuilder *> builders);
|
||||
|
||||
@@ -121,38 +96,6 @@ struct MFProcedureBuilder::Loop {
|
||||
MFDummyInstruction *end = nullptr;
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFInstructionCursor inline methods.
|
||||
*/
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction)
|
||||
: instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction)
|
||||
: instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction,
|
||||
bool branch_output)
|
||||
: instruction_(&instruction), branch_output_(branch_output)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction)
|
||||
: instruction_(&instruction)
|
||||
{
|
||||
}
|
||||
|
||||
inline MFInstructionCursor MFInstructionCursor::Entry()
|
||||
{
|
||||
MFInstructionCursor cursor;
|
||||
cursor.is_entry_ = true;
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* MFProcedureBuilder inline methods.
|
||||
*/
|
||||
@@ -253,7 +196,7 @@ inline void MFProcedureBuilder::add_output_parameter(MFVariable &variable)
|
||||
inline void MFProcedureBuilder::link_to_cursors(MFInstruction *instruction)
|
||||
{
|
||||
for (MFInstructionCursor &cursor : cursors_) {
|
||||
cursor.insert(*procedure_, instruction);
|
||||
cursor.set_next(*procedure_, instruction);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup fn
|
||||
*/
|
||||
|
||||
#include "FN_multi_function_procedure.hh"
|
||||
|
||||
namespace blender::fn::procedure_optimization {
|
||||
|
||||
void move_destructs_up(MFProcedure &procedure);
|
||||
|
||||
}
|
@@ -18,8 +18,13 @@
|
||||
#include "BLI_multi_value_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_stack.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "FN_field.hh"
|
||||
#include "FN_multi_function_parallel.hh"
|
||||
#include "FN_multi_function_procedure_optimization.hh"
|
||||
|
||||
#include "FN_field.hh"
|
||||
|
||||
namespace blender::fn {
|
||||
@@ -183,8 +188,8 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
|
||||
const Span<GField> operation_inputs = operation.inputs();
|
||||
|
||||
if (field_with_index.current_input_index < operation_inputs.size()) {
|
||||
/* Not all inputs are handled yet. Push the next input field to the stack and increment the
|
||||
* input index. */
|
||||
/* Not all inputs are handled yet. Push the next input field to the stack and increment
|
||||
* the input index. */
|
||||
fields_to_check.push({operation_inputs[field_with_index.current_input_index]});
|
||||
field_with_index.current_input_index++;
|
||||
}
|
||||
@@ -248,8 +253,8 @@ struct PartiallyInitializedArray : NonCopyable, NonMovable {
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate fields in the given context. If possible, multiple fields should be evaluated together,
|
||||
* because that can be more efficient when they share common sub-fields.
|
||||
* Evaluate fields in the given context. If possible, multiple fields should be evaluated
|
||||
* together, because that can be more efficient when they share common sub-fields.
|
||||
*
|
||||
* \param scope: The resource scope that owns data that makes up the output virtual arrays. Make
|
||||
* sure the scope is not destructed when the output virtual arrays are still used.
|
||||
@@ -271,6 +276,7 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
|
||||
const FieldContext &context,
|
||||
Span<GVMutableArray *> dst_varrays)
|
||||
{
|
||||
|
||||
Vector<const GVArray *> r_varrays(fields_to_evaluate.size(), nullptr);
|
||||
const int array_size = mask.min_array_size();
|
||||
|
||||
@@ -290,8 +296,8 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
|
||||
Vector<const GVArray *> field_context_inputs = get_field_context_inputs(
|
||||
scope, mask, context, field_tree_info.deduplicated_field_inputs);
|
||||
|
||||
/* Finish fields that output an input varray directly. For those we don't have to do any further
|
||||
* processing. */
|
||||
/* Finish fields that output an input varray directly. For those we don't have to do any
|
||||
* further processing. */
|
||||
for (const int out_index : fields_to_evaluate.index_range()) {
|
||||
const GFieldRef &field = fields_to_evaluate[out_index];
|
||||
if (!field.node().is_input()) {
|
||||
@@ -333,8 +339,14 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
|
||||
MFProcedure procedure;
|
||||
build_multi_function_procedure_for_fields(
|
||||
procedure, scope, field_tree_info, varying_fields_to_evaluate);
|
||||
// std::cout << procedure.to_dot() << "\n";
|
||||
// fn::procedure_optimization::move_destructs_up(procedure);
|
||||
// std::cout << procedure.to_dot() << "\n";
|
||||
MFProcedureExecutor procedure_executor{"Procedure", procedure};
|
||||
MFParamsBuilder mf_params{procedure_executor, array_size};
|
||||
fn::ParallelMultiFunction parallel_fn{procedure_executor, 10000};
|
||||
const MultiFunction &fn_to_execute = parallel_fn;
|
||||
|
||||
MFParamsBuilder mf_params{fn_to_execute, array_size};
|
||||
MFContextBuilder mf_context;
|
||||
|
||||
/* Provide inputs to the procedure executor. */
|
||||
@@ -376,7 +388,8 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
|
||||
mf_params.add_uninitialized_single_output(span);
|
||||
}
|
||||
|
||||
procedure_executor.call(mask, mf_params, mf_context);
|
||||
SCOPED_TIMER(__func__);
|
||||
fn_to_execute.call(mask, mf_params, mf_context);
|
||||
}
|
||||
|
||||
/* Evaluate constant fields if necessary. */
|
||||
@@ -419,8 +432,8 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
|
||||
procedure_executor.call(IndexRange(1), mf_params, mf_context);
|
||||
}
|
||||
|
||||
/* Copy data to supplied destination arrays if necessary. In some cases the evaluation above has
|
||||
* written the computed data in the right place already. */
|
||||
/* Copy data to supplied destination arrays if necessary. In some cases the evaluation above
|
||||
* has written the computed data in the right place already. */
|
||||
if (!dst_varrays.is_empty()) {
|
||||
for (const int out_index : fields_to_evaluate.index_range()) {
|
||||
GVMutableArray *output_varray = get_dst_varray_if_available(out_index);
|
||||
|
@@ -387,4 +387,43 @@ void GVMutableArray_GSpan::disable_not_applied_warning()
|
||||
show_not_saved_warning_ = false;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVArray_For_SlicedGVArray.
|
||||
*/
|
||||
|
||||
void GVArray_For_SlicedGVArray::get_impl(const int64_t index, void *r_value) const
|
||||
{
|
||||
varray_.get(index + offset_, r_value);
|
||||
}
|
||||
|
||||
void GVArray_For_SlicedGVArray::get_to_uninitialized_impl(const int64_t index, void *r_value) const
|
||||
{
|
||||
varray_.get_to_uninitialized(index + offset_, r_value);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GVArray_Slice.
|
||||
*/
|
||||
|
||||
GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice)
|
||||
{
|
||||
const CPPType &type = varray.type();
|
||||
if (varray.is_span()) {
|
||||
const GSpan span = varray.get_internal_span();
|
||||
varray_span_.emplace(span.slice(slice.start(), slice.size()));
|
||||
varray_ = &*varray_span_;
|
||||
}
|
||||
else if (varray.is_single()) {
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
|
||||
varray_->get_internal_single_to_uninitialized(buffer);
|
||||
varray_single_.emplace(type, slice.size(), buffer);
|
||||
type.destruct(buffer);
|
||||
varray_ = &*varray_single_;
|
||||
}
|
||||
else {
|
||||
varray_any_.emplace(varray, slice);
|
||||
varray_ = &*varray_any_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
||||
|
109
source/blender/functions/intern/multi_function_parallel.cc
Normal file
109
source/blender/functions/intern/multi_function_parallel.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "FN_multi_function_parallel.hh"
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
ParallelMultiFunction::ParallelMultiFunction(const MultiFunction &fn, const int64_t grain_size)
|
||||
: fn_(fn), grain_size_(grain_size)
|
||||
{
|
||||
this->set_signature(&fn.signature());
|
||||
|
||||
threading_supported_ = true;
|
||||
for (const int param_index : fn.param_indices()) {
|
||||
const MFParamType param_type = fn.param_type(param_index);
|
||||
if (param_type.data_type().category() == MFDataType::Vector) {
|
||||
threading_supported_ = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelMultiFunction::call(IndexMask mask, MFParams params, MFContext context) const
|
||||
{
|
||||
if (mask.size() <= grain_size_ || !threading_supported_) {
|
||||
fn_.call(mask, params, context);
|
||||
return;
|
||||
}
|
||||
|
||||
threading::parallel_for(mask.index_range(), grain_size_, [&](const IndexRange range) {
|
||||
const int size = range.size();
|
||||
IndexMask original_sub_mask{mask.indices().slice(range)};
|
||||
const int64_t offset = original_sub_mask.indices().first();
|
||||
const int64_t slice_size = original_sub_mask.indices().last() - offset + 1;
|
||||
const IndexRange slice_range{offset, slice_size};
|
||||
IndexMask sub_mask;
|
||||
Vector<int64_t> sub_mask_indices;
|
||||
if (original_sub_mask.is_range()) {
|
||||
sub_mask = IndexMask(size);
|
||||
}
|
||||
else {
|
||||
sub_mask_indices.resize(size);
|
||||
for (const int i : IndexRange(size)) {
|
||||
sub_mask_indices[i] = original_sub_mask[i] - offset;
|
||||
}
|
||||
sub_mask = sub_mask_indices.as_span();
|
||||
}
|
||||
|
||||
MFParamsBuilder sub_params{fn_, sub_mask.min_array_size()};
|
||||
ResourceScope scope;
|
||||
// static std::mutex mutex;
|
||||
// {
|
||||
// std::lock_guard lock{mutex};
|
||||
// std::cout << range << " " << sub_mask.min_array_size() << "\n";
|
||||
// }
|
||||
|
||||
for (const int param_index : fn_.param_indices()) {
|
||||
const MFParamType param_type = fn_.param_type(param_index);
|
||||
switch (param_type.category()) {
|
||||
case MFParamType::SingleInput: {
|
||||
const GVArray &varray = params.readonly_single_input(param_index);
|
||||
const GVArray &sliced_varray = scope.construct<GVArray_Slice>(
|
||||
"sliced varray", varray, slice_range);
|
||||
sub_params.add_readonly_single_input(sliced_varray);
|
||||
break;
|
||||
}
|
||||
case MFParamType::SingleMutable: {
|
||||
const GMutableSpan span = params.single_mutable(param_index);
|
||||
const GMutableSpan sliced_span = span.slice(slice_range.start(), slice_range.size());
|
||||
sub_params.add_single_mutable(sliced_span);
|
||||
break;
|
||||
}
|
||||
case MFParamType::SingleOutput: {
|
||||
const GMutableSpan span = params.uninitialized_single_output(param_index);
|
||||
const GMutableSpan sliced_span = span.slice(slice_range.start(), slice_range.size());
|
||||
sub_params.add_uninitialized_single_output(sliced_span);
|
||||
break;
|
||||
}
|
||||
case MFParamType::VectorInput:
|
||||
case MFParamType::VectorMutable:
|
||||
case MFParamType::VectorOutput: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn_.call(sub_mask, sub_params, context);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::fn
|
@@ -21,6 +21,65 @@
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
void MFInstructionCursor::set_next(MFProcedure &procedure, MFInstruction *new_instruction) const
|
||||
{
|
||||
switch (type_) {
|
||||
case Type::None: {
|
||||
break;
|
||||
}
|
||||
case Type::Entry: {
|
||||
procedure.set_entry(*new_instruction);
|
||||
break;
|
||||
}
|
||||
case Type::Call: {
|
||||
static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
case Type::Branch: {
|
||||
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(instruction_);
|
||||
if (branch_output_) {
|
||||
branch_instruction.set_branch_true(new_instruction);
|
||||
}
|
||||
else {
|
||||
branch_instruction.set_branch_false(new_instruction);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::Destruct: {
|
||||
static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
case Type::Dummy: {
|
||||
static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFInstruction *MFInstructionCursor::next(MFProcedure &procedure) const
|
||||
{
|
||||
switch (type_) {
|
||||
case Type::None:
|
||||
return nullptr;
|
||||
case Type::Entry:
|
||||
return procedure.entry();
|
||||
case Type::Call:
|
||||
return static_cast<MFCallInstruction *>(instruction_)->next();
|
||||
case Type::Branch: {
|
||||
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(instruction_);
|
||||
if (branch_output_) {
|
||||
return branch_instruction.branch_true();
|
||||
}
|
||||
return branch_instruction.branch_false();
|
||||
}
|
||||
case Type::Destruct:
|
||||
return static_cast<MFDestructInstruction *>(instruction_)->next();
|
||||
case Type::Dummy:
|
||||
return static_cast<MFDummyInstruction *>(instruction_)->next();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MFVariable::set_name(std::string name)
|
||||
{
|
||||
name_ = std::move(name);
|
||||
@@ -29,10 +88,10 @@ void MFVariable::set_name(std::string name)
|
||||
void MFCallInstruction::set_next(MFInstruction *instruction)
|
||||
{
|
||||
if (next_ != nullptr) {
|
||||
next_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
next_->prev_.remove_first_occurrence_and_reorder(*this);
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
instruction->prev_.append(*this);
|
||||
}
|
||||
next_ = instruction;
|
||||
}
|
||||
@@ -71,10 +130,10 @@ void MFBranchInstruction::set_condition(MFVariable *variable)
|
||||
void MFBranchInstruction::set_branch_true(MFInstruction *instruction)
|
||||
{
|
||||
if (branch_true_ != nullptr) {
|
||||
branch_true_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
branch_true_->prev_.remove_first_occurrence_and_reorder({*this, true});
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
instruction->prev_.append({*this, true});
|
||||
}
|
||||
branch_true_ = instruction;
|
||||
}
|
||||
@@ -82,10 +141,10 @@ void MFBranchInstruction::set_branch_true(MFInstruction *instruction)
|
||||
void MFBranchInstruction::set_branch_false(MFInstruction *instruction)
|
||||
{
|
||||
if (branch_false_ != nullptr) {
|
||||
branch_false_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
branch_false_->prev_.remove_first_occurrence_and_reorder({*this, false});
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
instruction->prev_.append({*this, false});
|
||||
}
|
||||
branch_false_ = instruction;
|
||||
}
|
||||
@@ -104,10 +163,10 @@ void MFDestructInstruction::set_variable(MFVariable *variable)
|
||||
void MFDestructInstruction::set_next(MFInstruction *instruction)
|
||||
{
|
||||
if (next_ != nullptr) {
|
||||
next_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
next_->prev_.remove_first_occurrence_and_reorder(*this);
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
instruction->prev_.append(*this);
|
||||
}
|
||||
next_ = instruction;
|
||||
}
|
||||
@@ -115,10 +174,10 @@ void MFDestructInstruction::set_next(MFInstruction *instruction)
|
||||
void MFDummyInstruction::set_next(MFInstruction *instruction)
|
||||
{
|
||||
if (next_ != nullptr) {
|
||||
next_->prev_.remove_first_occurrence_and_reorder(this);
|
||||
next_->prev_.remove_first_occurrence_and_reorder(*this);
|
||||
}
|
||||
if (instruction != nullptr) {
|
||||
instruction->prev_.append(this);
|
||||
instruction->prev_.append(*this);
|
||||
}
|
||||
next_ = instruction;
|
||||
}
|
||||
@@ -420,7 +479,11 @@ MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction
|
||||
|
||||
Set<const MFInstruction *> checked_instructions;
|
||||
Stack<const MFInstruction *> instructions_to_check;
|
||||
instructions_to_check.push_multiple(target_instruction.prev_);
|
||||
for (const MFInstructionCursor &cursor : target_instruction.prev_) {
|
||||
if (cursor.instruction() != nullptr) {
|
||||
instructions_to_check.push(cursor.instruction());
|
||||
}
|
||||
}
|
||||
|
||||
while (!instructions_to_check.is_empty()) {
|
||||
const MFInstruction &instruction = *instructions_to_check.pop();
|
||||
@@ -467,7 +530,11 @@ MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction
|
||||
if (&instruction == entry_) {
|
||||
check_entry_instruction();
|
||||
}
|
||||
instructions_to_check.push_multiple(instruction.prev_);
|
||||
for (const MFInstructionCursor &cursor : instruction.prev_) {
|
||||
if (cursor.instruction() != nullptr) {
|
||||
instructions_to_check.push(cursor.instruction());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,13 +674,10 @@ class MFProcedureDotExport {
|
||||
|
||||
bool has_to_be_block_begin(const MFInstruction &instruction)
|
||||
{
|
||||
if (procedure_.entry() == &instruction) {
|
||||
return true;
|
||||
}
|
||||
if (instruction.prev().size() != 1) {
|
||||
return true;
|
||||
}
|
||||
if (instruction.prev()[0]->type() == MFInstructionType::Branch) {
|
||||
if (instruction.prev()[0].type() == MFInstructionCursor::Type::Branch) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -623,7 +687,7 @@ class MFProcedureDotExport {
|
||||
{
|
||||
const MFInstruction *current = &representative;
|
||||
while (!this->has_to_be_block_begin(*current)) {
|
||||
current = current->prev()[0];
|
||||
current = current->prev()[0].instruction();
|
||||
if (current == &representative) {
|
||||
/* There is a loop without entry or exit, just break it up here. */
|
||||
break;
|
||||
|
@@ -18,50 +18,6 @@
|
||||
|
||||
namespace blender::fn {
|
||||
|
||||
void MFInstructionCursor::insert(MFProcedure &procedure, MFInstruction *new_instruction)
|
||||
{
|
||||
if (instruction_ == nullptr) {
|
||||
if (is_entry_) {
|
||||
procedure.set_entry(*new_instruction);
|
||||
}
|
||||
else {
|
||||
/* The cursors points at nothing, nothing to do. */
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (instruction_->type()) {
|
||||
case MFInstructionType::Call: {
|
||||
static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Branch: {
|
||||
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(
|
||||
instruction_);
|
||||
if (branch_output_) {
|
||||
branch_instruction.set_branch_true(new_instruction);
|
||||
}
|
||||
else {
|
||||
branch_instruction.set_branch_false(new_instruction);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Destruct: {
|
||||
static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Dummy: {
|
||||
static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction);
|
||||
break;
|
||||
}
|
||||
case MFInstructionType::Return: {
|
||||
/* It shouldn't be possible to build a cursor that points to a return instruction. */
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MFProcedureBuilder::add_destruct(MFVariable &variable)
|
||||
{
|
||||
MFDestructInstruction &instruction = procedure_->new_destruct_instruction();
|
||||
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "FN_multi_function_procedure_optimization.hh"
|
||||
|
||||
namespace blender::fn::procedure_optimization {
|
||||
|
||||
static bool uses_variable(const MFInstruction &instr, const MFVariable &variable)
|
||||
{
|
||||
switch (instr.type()) {
|
||||
case MFInstructionType::Branch:
|
||||
return static_cast<const MFBranchInstruction &>(instr).condition() == &variable;
|
||||
case MFInstructionType::Call:
|
||||
return static_cast<const MFCallInstruction &>(instr).params().contains(&variable);
|
||||
case MFInstructionType::Destruct:
|
||||
return static_cast<const MFDestructInstruction &>(instr).variable() == &variable;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void move_destructs_up(MFProcedure &procedure)
|
||||
{
|
||||
for (MFDestructInstruction *destruct_instr : procedure.destruct_instructions()) {
|
||||
MFVariable *variable = destruct_instr->variable();
|
||||
if (variable == nullptr) {
|
||||
continue;
|
||||
}
|
||||
MFInstruction *last_use_in_block_instr = nullptr;
|
||||
MFInstruction *current_instr = destruct_instr;
|
||||
while (current_instr->prev().size() == 1) {
|
||||
current_instr = current_instr->prev()[0].instruction();
|
||||
if (current_instr == nullptr) {
|
||||
break;
|
||||
}
|
||||
if (uses_variable(*current_instr, *variable)) {
|
||||
last_use_in_block_instr = current_instr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (last_use_in_block_instr == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (last_use_in_block_instr->type() == MFInstructionType::Call) {
|
||||
MFCallInstruction &call_instr = static_cast<MFCallInstruction &>(*last_use_in_block_instr);
|
||||
destruct_instr->prev()[0].set_next(procedure, destruct_instr->next());
|
||||
destruct_instr->set_next(call_instr.next());
|
||||
call_instr.set_next(destruct_instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::fn::procedure_optimization
|
@@ -2,8 +2,11 @@
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "FN_multi_function.hh"
|
||||
#include "FN_multi_function_builder.hh"
|
||||
#include "FN_multi_function_parallel.hh"
|
||||
#include "FN_multi_function_test_common.hh"
|
||||
|
||||
namespace blender::fn::tests {
|
||||
@@ -328,5 +331,29 @@ TEST(multi_function, CustomMF_Convert)
|
||||
EXPECT_EQ(outputs[2], 9);
|
||||
}
|
||||
|
||||
TEST(multi_function, Parallel)
|
||||
{
|
||||
CustomMF_SI_SI_SO<float, float, float> add_fn{
|
||||
"add", [](float a, float b) { return std::tan(std::sin(a)) * std::tanh(std::cos(b)); }};
|
||||
ParallelMultiFunction parallel_fn{add_fn, int64_t(1e5)};
|
||||
const MultiFunction &fn_to_evaluate = parallel_fn;
|
||||
|
||||
const int amount = 1e8;
|
||||
Array<float> inputs_a(amount, 1);
|
||||
Array<float> inputs_b(amount, 1);
|
||||
Array<float> outputs(amount, 1);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
SCOPED_TIMER(__func__);
|
||||
MFParamsBuilder params(fn_to_evaluate, amount);
|
||||
params.add_readonly_single_input(inputs_a.as_span());
|
||||
params.add_readonly_single_input(inputs_b.as_span());
|
||||
params.add_uninitialized_single_output(outputs.as_mutable_span());
|
||||
|
||||
MFContextBuilder context;
|
||||
fn_to_evaluate.call(IndexRange(amount), params, context);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace blender::fn::tests
|
||||
|
@@ -118,7 +118,7 @@ void ABCAbstractWriter::update_bounding_box(Object *object)
|
||||
|
||||
if (!bb) {
|
||||
if (object->type != OB_CAMERA) {
|
||||
CLOG_WARN(&LOG, "Bounding box is null!\n");
|
||||
CLOG_WARN(&LOG, "Bounding box is null!");
|
||||
}
|
||||
bounding_box_.min.x = bounding_box_.min.y = bounding_box_.min.z = 0;
|
||||
bounding_box_.max.x = bounding_box_.max.y = bounding_box_.max.z = 0;
|
||||
|
@@ -1942,6 +1942,7 @@ typedef struct MeshCacheModifierData {
|
||||
|
||||
float factor;
|
||||
char deform_mode;
|
||||
char defgrp_name[64];
|
||||
char _pad[7];
|
||||
|
||||
/* play_mode == MOD_MESHCACHE_PLAY_CFEA */
|
||||
@@ -1958,6 +1959,11 @@ typedef struct MeshCacheModifierData {
|
||||
char filepath[1024];
|
||||
} MeshCacheModifierData;
|
||||
|
||||
/* MeshCache modifier flags. */
|
||||
enum {
|
||||
MOD_MESHCACHE_INVERT_VERTEX_GROUP = 1 << 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
MOD_MESHCACHE_TYPE_MDD = 1,
|
||||
MOD_MESHCACHE_TYPE_PC2 = 2,
|
||||
|
@@ -788,7 +788,7 @@ bool RNA_struct_override_matches(Main *bmain,
|
||||
continue;
|
||||
}
|
||||
|
||||
CLOG_INFO(&LOG, 5, "Override Checking %s\n", rna_path);
|
||||
CLOG_INFO(&LOG, 5, "Override Checking %s", rna_path);
|
||||
|
||||
IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(override, rna_path);
|
||||
if (ignore_overridden && op != NULL) {
|
||||
|
@@ -755,6 +755,7 @@ RNA_MOD_VGROUP_NAME_SET(LaplacianDeform, anchor_grp_name);
|
||||
RNA_MOD_VGROUP_NAME_SET(LaplacianSmooth, defgrp_name);
|
||||
RNA_MOD_VGROUP_NAME_SET(Lattice, name);
|
||||
RNA_MOD_VGROUP_NAME_SET(Mask, vgroup);
|
||||
RNA_MOD_VGROUP_NAME_SET(MeshCache, defgrp_name);
|
||||
RNA_MOD_VGROUP_NAME_SET(MeshDeform, defgrp_name);
|
||||
RNA_MOD_VGROUP_NAME_SET(NormalEdit, defgrp_name);
|
||||
RNA_MOD_VGROUP_NAME_SET(Shrinkwrap, vgroup_name);
|
||||
@@ -6045,6 +6046,20 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Influence", "Influence of the deformation");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "defgrp_name");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Vertex Group",
|
||||
"Name of the Vertex Group which determines the influence of the modifier per point");
|
||||
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshCacheModifier_defgrp_name_set");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_MESHCACHE_INVERT_VERTEX_GROUP);
|
||||
RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Axis Conversion */
|
||||
prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE);
|
||||
|
@@ -224,6 +224,12 @@ static EnumPropertyItem instance_items_empty[] = {
|
||||
INSTANCE_ITEM_COLLECTION,
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
static EnumPropertyItem instance_items_font[] = {
|
||||
{0, "NONE", 0, "None", ""},
|
||||
{OB_DUPLIVERTS, "VERTS", 0, "Vertices", "Use Object Font on characters"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
#endif
|
||||
#undef INSTANCE_ITEMS_SHARED
|
||||
#undef INSTANCE_ITEM_COLLECTION
|
||||
@@ -762,6 +768,9 @@ static const EnumPropertyItem *rna_Object_instance_type_itemf(bContext *UNUSED(C
|
||||
else if (ob->type == OB_POINTCLOUD) {
|
||||
item = instance_items_pointcloud;
|
||||
}
|
||||
else if (ob->type == OB_FONT) {
|
||||
item = instance_items_font;
|
||||
}
|
||||
else {
|
||||
item = instance_items_nogroup;
|
||||
}
|
||||
|
@@ -845,6 +845,17 @@ static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *scene, Pointer
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
|
||||
}
|
||||
|
||||
static void rna_Sequence_pan_range(
|
||||
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
|
||||
{
|
||||
Scene *scene = (Scene *)ptr->owner_id;
|
||||
|
||||
*min = -FLT_MAX;
|
||||
*max = FLT_MAX;
|
||||
*softmax = 1 + (int)(scene->r.ffcodecdata.audio_channels > 2);
|
||||
*softmin = -*softmax;
|
||||
}
|
||||
|
||||
static int rna_Sequence_input_count_get(PointerRNA *ptr)
|
||||
{
|
||||
Sequence *seq = (Sequence *)(ptr->data);
|
||||
@@ -2559,8 +2570,10 @@ static void rna_def_sound(BlenderRNA *brna)
|
||||
|
||||
prop = RNA_def_property(srna, "pan", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "pan");
|
||||
RNA_def_property_range(prop, -2.0f, 2.0f);
|
||||
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, -2, 2, 1, 2);
|
||||
RNA_def_property_ui_text(prop, "Pan", "Playback panning of the sound (only for Mono sources)");
|
||||
RNA_def_property_float_funcs(prop, NULL, NULL, "rna_Sequence_pan_range");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_audio_update");
|
||||
|
||||
prop = RNA_def_property(srna, "show_waveform", PROP_BOOLEAN, PROP_NONE);
|
||||
|
@@ -36,8 +36,11 @@
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_deform.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_wrapper.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
@@ -53,6 +56,7 @@
|
||||
#include "MOD_meshcache_util.h" /* utility functions */
|
||||
#include "MOD_modifiertypes.h"
|
||||
#include "MOD_ui_common.h"
|
||||
#include "MOD_util.h"
|
||||
|
||||
static void initData(ModifierData *md)
|
||||
{
|
||||
@@ -84,11 +88,16 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
|
||||
static void meshcache_do(MeshCacheModifierData *mcmd,
|
||||
Scene *scene,
|
||||
Object *ob,
|
||||
Mesh *mesh,
|
||||
float (*vertexCos_Real)[3],
|
||||
int numVerts)
|
||||
{
|
||||
const bool use_factor = mcmd->factor < 1.0f;
|
||||
float(*vertexCos_Store)[3] = (use_factor ||
|
||||
int influence_group_index;
|
||||
MDeformVert *dvert;
|
||||
MOD_get_vgroup(ob, mesh, mcmd->defgrp_name, &dvert, &influence_group_index);
|
||||
|
||||
float(*vertexCos_Store)[3] = (use_factor || influence_group_index != -1 ||
|
||||
(mcmd->deform_mode == MOD_MESHCACHE_DEFORM_INTEGRATE)) ?
|
||||
MEM_malloc_arrayN(
|
||||
numVerts, sizeof(*vertexCos_Store), __func__) :
|
||||
@@ -256,7 +265,29 @@ static void meshcache_do(MeshCacheModifierData *mcmd,
|
||||
|
||||
if (vertexCos_Store) {
|
||||
if (ok) {
|
||||
if (use_factor) {
|
||||
if (influence_group_index != -1) {
|
||||
const float global_factor = (mcmd->flag & MOD_MESHCACHE_INVERT_VERTEX_GROUP) ?
|
||||
-mcmd->factor :
|
||||
mcmd->factor;
|
||||
const float global_offset = (mcmd->flag & MOD_MESHCACHE_INVERT_VERTEX_GROUP) ?
|
||||
mcmd->factor :
|
||||
0.0f;
|
||||
if (mesh->dvert != NULL) {
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
/* For each vertex, compute its blending factor between the mesh cache (for `fac = 0`)
|
||||
* and the former position of the vertex (for `fac = 1`). */
|
||||
const MDeformVert *currentIndexDVert = dvert + i;
|
||||
const float local_vertex_fac = global_offset +
|
||||
BKE_defvert_find_weight(currentIndexDVert,
|
||||
influence_group_index) *
|
||||
global_factor;
|
||||
interp_v3_v3v3(
|
||||
vertexCos_Real[i], vertexCos_Real[i], vertexCos_Store[i], local_vertex_fac);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (use_factor) {
|
||||
/* Influence_group_index is -1. */
|
||||
interp_vn_vn(*vertexCos_Real, *vertexCos_Store, mcmd->factor, numVerts * 3);
|
||||
}
|
||||
else {
|
||||
@@ -270,34 +301,59 @@ static void meshcache_do(MeshCacheModifierData *mcmd,
|
||||
|
||||
static void deformVerts(ModifierData *md,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *UNUSED(mesh),
|
||||
Mesh *mesh,
|
||||
float (*vertexCos)[3],
|
||||
int numVerts)
|
||||
{
|
||||
MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md;
|
||||
Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
|
||||
|
||||
meshcache_do(mcmd, scene, ctx->object, vertexCos, numVerts);
|
||||
Mesh *mesh_src = NULL;
|
||||
|
||||
if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') {
|
||||
/* `mesh_src` is only needed for vertex groups. */
|
||||
mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, numVerts, false, false);
|
||||
}
|
||||
meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, numVerts);
|
||||
|
||||
if (!ELEM(mesh_src, NULL, mesh)) {
|
||||
BKE_id_free(NULL, mesh_src);
|
||||
}
|
||||
}
|
||||
|
||||
static void deformVertsEM(ModifierData *md,
|
||||
const ModifierEvalContext *ctx,
|
||||
struct BMEditMesh *UNUSED(editData),
|
||||
Mesh *UNUSED(mesh),
|
||||
struct BMEditMesh *editData,
|
||||
Mesh *mesh,
|
||||
float (*vertexCos)[3],
|
||||
int numVerts)
|
||||
{
|
||||
MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md;
|
||||
Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
|
||||
|
||||
meshcache_do(mcmd, scene, ctx->object, vertexCos, numVerts);
|
||||
Mesh *mesh_src = NULL;
|
||||
|
||||
if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') {
|
||||
/* `mesh_src` is only needed for vertex groups. */
|
||||
mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, numVerts, false, false);
|
||||
}
|
||||
if (mesh_src != NULL) {
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh_src);
|
||||
}
|
||||
|
||||
meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, numVerts);
|
||||
|
||||
if (!ELEM(mesh_src, NULL, mesh)) {
|
||||
BKE_id_free(NULL, mesh_src);
|
||||
}
|
||||
}
|
||||
|
||||
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||
{
|
||||
uiLayout *layout = panel->layout;
|
||||
|
||||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
|
||||
PointerRNA ob_ptr;
|
||||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
|
||||
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
|
||||
@@ -307,6 +363,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||
uiItemR(layout, ptr, "factor", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "deform_mode", 0, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "interpolation", 0, NULL, ICON_NONE);
|
||||
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL);
|
||||
|
||||
modifier_panel_end(layout, ptr);
|
||||
}
|
||||
|
@@ -330,8 +330,8 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
|
||||
ui_data->max = ui_data->soft_max = (double)value->max;
|
||||
ui_data->default_array = (double *)MEM_mallocN(sizeof(double[3]), "mod_prop_default");
|
||||
ui_data->default_array_len = 3;
|
||||
for (int i = 3; i < 3; i++) {
|
||||
ui_data->default_array[i] = (double)value->value[i];
|
||||
for (const int i : IndexRange(3)) {
|
||||
ui_data->default_array[i] = double(value->value[i]);
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
@@ -468,7 +468,7 @@ static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict)
|
||||
Py_DECREF(list);
|
||||
}
|
||||
else {
|
||||
PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->step));
|
||||
PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->default_value));
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
@@ -499,7 +499,7 @@ static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict)
|
||||
Py_DECREF(list);
|
||||
}
|
||||
else {
|
||||
PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->step));
|
||||
PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->default_value));
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
|
@@ -485,7 +485,7 @@ static int pygpu_buffer__sq_ass_item(BPyGPUBuffer *self, int i, PyObject *v)
|
||||
case GPU_DATA_UINT:
|
||||
case GPU_DATA_UINT_24_8:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return PyArg_Parse(v, "b:Expected ints", &self->buf.as_uint[i]) ? 0 : -1;
|
||||
return PyArg_Parse(v, "I:Expected unsigned ints", &self->buf.as_uint[i]) ? 0 : -1;
|
||||
default:
|
||||
return 0; /* should never happen */
|
||||
}
|
||||
|
@@ -753,7 +753,7 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *
|
||||
CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not a valid type", member);
|
||||
}
|
||||
else {
|
||||
CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not found\n", member);
|
||||
CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not found", member);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@@ -3749,7 +3749,7 @@ wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm,
|
||||
const char *keymap_id = NULL;
|
||||
|
||||
/* Support for the gizmo owning the tool keymap. */
|
||||
if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\n') {
|
||||
if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\0') {
|
||||
wmGizmoMap *gzmap = NULL;
|
||||
wmGizmoGroup *gzgroup = NULL;
|
||||
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
|
||||
|
@@ -705,7 +705,7 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
|
||||
}
|
||||
|
||||
if (failure) {
|
||||
CLOG_ERROR(&LOG, "Failed to get buffer, %s\n", err_out);
|
||||
CLOG_ERROR(&LOG, "Failed to get buffer, %s", err_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@ class TestEntry:
|
||||
category: str = ''
|
||||
revision: str = ''
|
||||
git_hash: str = ''
|
||||
environment: Dict = field(default_factory=dict)
|
||||
executable: str = ''
|
||||
date: int = 0
|
||||
device_type: str = 'CPU'
|
||||
@@ -160,7 +161,13 @@ class TestConfig:
|
||||
def read_blender_executables(env, name) -> List:
|
||||
config = TestConfig._read_config_module(env.base_dir / name)
|
||||
builds = getattr(config, 'builds', {})
|
||||
return [pathlib.Path(build) for build in builds.values()]
|
||||
executables = []
|
||||
|
||||
for executable in builds.values():
|
||||
executable, _ = TestConfig._split_environment_variables(executable)
|
||||
executables.append(pathlib.Path(executable))
|
||||
|
||||
return executables
|
||||
|
||||
@staticmethod
|
||||
def _read_config_module(base_dir: pathlib.Path) -> None:
|
||||
@@ -191,9 +198,10 @@ class TestConfig:
|
||||
|
||||
# Get entries for specified commits, tags and branches.
|
||||
for revision_name, revision_commit in self.revisions.items():
|
||||
revision_commit, environment = self._split_environment_variables(revision_commit)
|
||||
git_hash = env.resolve_git_hash(revision_commit)
|
||||
date = env.git_hash_date(git_hash)
|
||||
entries += self._get_entries(revision_name, git_hash, '', date)
|
||||
entries += self._get_entries(revision_name, git_hash, '', environment, date)
|
||||
|
||||
# Optimization to avoid rebuilds.
|
||||
revisions_to_build = set()
|
||||
@@ -204,6 +212,7 @@ class TestConfig:
|
||||
|
||||
# Get entries for revisions based on existing builds.
|
||||
for revision_name, executable in self.builds.items():
|
||||
executable, environment = self._split_environment_variables(executable)
|
||||
executable_path = env._blender_executable_from_path(pathlib.Path(executable))
|
||||
if not executable_path:
|
||||
sys.stderr.write(f'Error: build {executable} not found\n')
|
||||
@@ -214,7 +223,7 @@ class TestConfig:
|
||||
env.set_default_blender_executable()
|
||||
|
||||
mtime = executable_path.stat().st_mtime
|
||||
entries += self._get_entries(revision_name, git_hash, executable, mtime)
|
||||
entries += self._get_entries(revision_name, git_hash, executable, environment, mtime)
|
||||
|
||||
# Detect number of categories for more compact printing.
|
||||
categories = set()
|
||||
@@ -229,6 +238,7 @@ class TestConfig:
|
||||
revision_name: str,
|
||||
git_hash: str,
|
||||
executable: pathlib.Path,
|
||||
environment: str,
|
||||
date: int) -> None:
|
||||
entries = []
|
||||
for test in self.tests.tests:
|
||||
@@ -241,10 +251,12 @@ class TestConfig:
|
||||
# Test if revision hash or executable changed.
|
||||
if entry.git_hash != git_hash or \
|
||||
entry.executable != executable or \
|
||||
entry.environment != environment or \
|
||||
entry.benchmark_type != self.benchmark_type or \
|
||||
entry.date != date:
|
||||
# Update existing entry.
|
||||
entry.git_hash = git_hash
|
||||
entry.environment = environment
|
||||
entry.executable = executable
|
||||
entry.benchmark_type = self.benchmark_type
|
||||
entry.date = date
|
||||
@@ -256,6 +268,7 @@ class TestConfig:
|
||||
revision=revision_name,
|
||||
git_hash=git_hash,
|
||||
executable=executable,
|
||||
environment=environment,
|
||||
date=date,
|
||||
test=test_name,
|
||||
category=test_category,
|
||||
@@ -266,3 +279,10 @@ class TestConfig:
|
||||
entries.append(entry)
|
||||
|
||||
return entries
|
||||
|
||||
@staticmethod
|
||||
def _split_environment_variables(revision):
|
||||
if isinstance(revision, str):
|
||||
return revision, {}
|
||||
else:
|
||||
return revision[0], revision[1]
|
||||
|
@@ -104,9 +104,10 @@ class TestEnvironment:
|
||||
self._init_default_blender_executable()
|
||||
return True
|
||||
|
||||
def set_blender_executable(self, executable_path: pathlib.Path) -> None:
|
||||
def set_blender_executable(self, executable_path: pathlib.Path, environment: Dict = {}) -> None:
|
||||
# Run all Blender commands with this executable.
|
||||
self.blender_executable = executable_path
|
||||
self.blender_executable_environment = environment
|
||||
|
||||
def _blender_executable_name(self) -> pathlib.Path:
|
||||
if platform.system() == "Windows":
|
||||
@@ -150,6 +151,7 @@ class TestEnvironment:
|
||||
|
||||
def set_default_blender_executable(self) -> None:
|
||||
self.blender_executable = self.default_blender_executable
|
||||
self.blender_executable_environment = {}
|
||||
|
||||
def set_log_file(self, filepath: pathlib.Path, clear=True) -> None:
|
||||
# Log all commands and output to this file.
|
||||
@@ -161,7 +163,7 @@ class TestEnvironment:
|
||||
def unset_log_file(self) -> None:
|
||||
self.log_file = None
|
||||
|
||||
def call(self, args: List[str], cwd: pathlib.Path, silent=False) -> List[str]:
|
||||
def call(self, args: List[str], cwd: pathlib.Path, silent: bool=False, environment: Dict={}) -> List[str]:
|
||||
# Execute command with arguments in specified directory,
|
||||
# and return combined stdout and stderr output.
|
||||
|
||||
@@ -173,7 +175,13 @@ class TestEnvironment:
|
||||
f = open(self.log_file, 'a')
|
||||
f.write('\n' + ' '.join([str(arg) for arg in args]) + '\n\n')
|
||||
|
||||
proc = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
env = os.environ
|
||||
if len(environment):
|
||||
env = env.copy()
|
||||
for key, value in environment.items():
|
||||
env[key] = value
|
||||
|
||||
proc = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
|
||||
|
||||
# Read line by line
|
||||
lines = []
|
||||
@@ -208,7 +216,8 @@ class TestEnvironment:
|
||||
else:
|
||||
common_args += ['--background']
|
||||
|
||||
return self.call([self.blender_executable] + common_args + args, cwd=self.base_dir)
|
||||
return self.call([self.blender_executable] + common_args + args, cwd=self.base_dir,
|
||||
environment=self.blender_executable_environment)
|
||||
|
||||
def run_in_blender(self,
|
||||
function: Callable[[Dict], Dict],
|
||||
|
@@ -42,7 +42,7 @@ class TestGraph:
|
||||
|
||||
# Generate one graph for every device x category x result key combination.
|
||||
for category, category_entries in categories.items():
|
||||
entries = sorted(category_entries, key=lambda entry: (entry.revision, entry.test))
|
||||
entries = sorted(category_entries, key=lambda entry: (entry.revision, entry.test, entry.date))
|
||||
|
||||
outputs = set()
|
||||
for entry in entries:
|
||||
@@ -58,8 +58,6 @@ class TestGraph:
|
||||
self.json = json.dumps(data, indent=2)
|
||||
|
||||
def chart(self, device_name: str, chart_name: str, entries: List, chart_type: str, output: str) -> Dict:
|
||||
entries = sorted(entries, key=lambda entry: entry.date)
|
||||
|
||||
# Gather used tests.
|
||||
tests = {}
|
||||
for entry in entries:
|
||||
|
@@ -83,15 +83,20 @@ def match_entry(entry: api.TestEntry, args: argparse.Namespace):
|
||||
entry.test.find(args.test) != -1 or \
|
||||
entry.category.find(args.test) != -1
|
||||
|
||||
def run_entry(env: api.TestEnvironment, config: api.TestConfig, row: List, entry: api.TestEntry):
|
||||
def run_entry(env: api.TestEnvironment,
|
||||
config: api.TestConfig,
|
||||
row: List,
|
||||
entry: api.TestEntry,
|
||||
update_only: bool):
|
||||
# Check if entry needs to be run.
|
||||
if entry.status not in ('queued', 'outdated'):
|
||||
if update_only and entry.status not in ('queued', 'outdated'):
|
||||
print_row(config, row, end='\r')
|
||||
return False
|
||||
|
||||
# Run test entry.
|
||||
revision = entry.revision
|
||||
git_hash = entry.git_hash
|
||||
environment = entry.environment
|
||||
testname = entry.test
|
||||
testcategory = entry.category
|
||||
device_type = entry.device_type
|
||||
@@ -116,13 +121,15 @@ def run_entry(env: api.TestEnvironment, config: api.TestConfig, row: List, entry
|
||||
print_row(config, row, end='\r')
|
||||
executable_ok = True
|
||||
if len(entry.executable):
|
||||
env.set_blender_executable(pathlib.Path(entry.executable))
|
||||
env.set_blender_executable(pathlib.Path(entry.executable), environment)
|
||||
else:
|
||||
env.checkout(git_hash)
|
||||
executable_ok = env.build()
|
||||
if not executable_ok:
|
||||
entry.status = 'failed'
|
||||
entry.error_msg = 'Failed to build'
|
||||
else:
|
||||
env.set_blender_executable(env.blender_executable, environment)
|
||||
|
||||
# Run test and update output and status.
|
||||
if executable_ok:
|
||||
@@ -219,7 +226,7 @@ def cmd_reset(env: api.TestEnvironment, argv: List):
|
||||
|
||||
config.queue.write()
|
||||
|
||||
def cmd_run(env: api.TestEnvironment, argv: List):
|
||||
def cmd_run(env: api.TestEnvironment, argv: List, update_only: bool):
|
||||
# Run tests.
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('config', nargs='?', default=None)
|
||||
@@ -233,7 +240,7 @@ def cmd_run(env: api.TestEnvironment, argv: List):
|
||||
for row in config.queue.rows(use_revision_columns(config)):
|
||||
if match_entry(row[0], args):
|
||||
for entry in row:
|
||||
if run_entry(env, config, row, entry):
|
||||
if run_entry(env, config, row, entry, update_only):
|
||||
updated = True
|
||||
# Write queue every time in case running gets interrupted,
|
||||
# so it can be resumed.
|
||||
@@ -268,8 +275,9 @@ def main():
|
||||
' \n'
|
||||
' list List available tests, devices and configurations\n'
|
||||
' \n'
|
||||
' run [<config>] [<test>] Execute tests for configuration\n'
|
||||
' reset [<config>] [<test>] Clear tests results from config, for re-running\n'
|
||||
' run [<config>] [<test>] Execute all tests in configuration\n'
|
||||
' update [<config>] [<test>] Execute only queued and outdated tests\n'
|
||||
' reset [<config>] [<test>] Clear tests results in configuration\n'
|
||||
' status [<config>] [<test>] List configurations and their tests\n'
|
||||
' \n'
|
||||
' graph a.json b.json... -o out.html Create graph from results in JSON files\n')
|
||||
@@ -304,7 +312,9 @@ def main():
|
||||
if args.command == 'list':
|
||||
cmd_list(env, argv)
|
||||
elif args.command == 'run':
|
||||
cmd_run(env, argv)
|
||||
cmd_run(env, argv, update_only=False)
|
||||
elif args.command == 'update':
|
||||
cmd_run(env, argv, update_only=True)
|
||||
elif args.command == 'reset':
|
||||
cmd_reset(env, argv)
|
||||
elif args.command == 'status':
|
||||
|
Reference in New Issue
Block a user