From 120a38ccbe0ef4702cc35f9e0cfb841368913eec Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 16 Mar 2020 18:25:23 +0100 Subject: Nodes: Display bl_icon of custom nodes in node header This is D1578 by Philipp Oeser with small modifications. --- source/blender/editors/space_node/node_draw.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 35fbbad14b0..d2258db074d 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1181,6 +1181,25 @@ static void node_draw_basis(const bContext *C, UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_group_edit"); UI_block_emboss_set(node->block, UI_EMBOSS); } + if (node->type == NODE_CUSTOM && node->typeinfo->ui_icon != ICON_NONE) { + iconofs -= iconbutw; + UI_block_emboss_set(node->block, UI_EMBOSS_NONE); + uiDefIconBut(node->block, + UI_BTYPE_BUT, + 0, + node->typeinfo->ui_icon, + iconofs, + rct->ymax - NODE_DY, + iconbutw, + UI_UNIT_Y, + NULL, + 0, + 0, + 0, + 0, + ""); + UI_block_emboss_set(node->block, UI_EMBOSS); + } /* title */ if (node->flag & SELECT) { -- cgit v1.2.3 From 47af235ba987e4c4dd8ee3eafce61422986dc3ad Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Mar 2020 10:32:48 +1100 Subject: Revert "UI: Add 7 new community themes" This reverts commit d89e5fcaef5135360716053a43c4676508eaf5ef. This was meant to be committed to the add-ons repo. --- .../scripts/presets/interface_theme/deep_grey.xml | 1481 -------------------- release/scripts/presets/interface_theme/maya.xml | 1481 -------------------- .../presets/interface_theme/minimal_dark.xml | 1481 -------------------- release/scripts/presets/interface_theme/modo.xml | 1481 -------------------- .../presets/interface_theme/print_friendly.xml | 1481 -------------------- release/scripts/presets/interface_theme/white.xml | 1470 ------------------- release/scripts/presets/interface_theme/xsi.xml | 1481 -------------------- 7 files changed, 10356 deletions(-) delete mode 100644 release/scripts/presets/interface_theme/deep_grey.xml delete mode 100644 release/scripts/presets/interface_theme/maya.xml delete mode 100644 release/scripts/presets/interface_theme/minimal_dark.xml delete mode 100644 release/scripts/presets/interface_theme/modo.xml delete mode 100644 release/scripts/presets/interface_theme/print_friendly.xml delete mode 100644 release/scripts/presets/interface_theme/white.xml delete mode 100644 release/scripts/presets/interface_theme/xsi.xml diff --git a/release/scripts/presets/interface_theme/deep_grey.xml b/release/scripts/presets/interface_theme/deep_grey.xml deleted file mode 100644 index d6a78997eec..00000000000 --- a/release/scripts/presets/interface_theme/deep_grey.xml +++ /dev/null @@ -1,1481 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/release/scripts/presets/interface_theme/maya.xml b/release/scripts/presets/interface_theme/maya.xml deleted file mode 100644 index 1e93f3a0993..00000000000 --- a/release/scripts/presets/interface_theme/maya.xml +++ /dev/null @@ -1,1481 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/release/scripts/presets/interface_theme/minimal_dark.xml b/release/scripts/presets/interface_theme/minimal_dark.xml deleted file mode 100644 index b61589d6e84..00000000000 --- a/release/scripts/presets/interface_theme/minimal_dark.xml +++ /dev/null @@ -1,1481 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/release/scripts/presets/interface_theme/modo.xml b/release/scripts/presets/interface_theme/modo.xml deleted file mode 100644 index b98d00d7152..00000000000 --- a/release/scripts/presets/interface_theme/modo.xml +++ /dev/null @@ -1,1481 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/release/scripts/presets/interface_theme/print_friendly.xml b/release/scripts/presets/interface_theme/print_friendly.xml deleted file mode 100644 index b607e29bbe4..00000000000 --- a/release/scripts/presets/interface_theme/print_friendly.xml +++ /dev/null @@ -1,1481 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/release/scripts/presets/interface_theme/white.xml b/release/scripts/presets/interface_theme/white.xml deleted file mode 100644 index 26ddf374c22..00000000000 --- a/release/scripts/presets/interface_theme/white.xml +++ /dev/null @@ -1,1470 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/release/scripts/presets/interface_theme/xsi.xml b/release/scripts/presets/interface_theme/xsi.xml deleted file mode 100644 index 4ed66a2a2c6..00000000000 --- a/release/scripts/presets/interface_theme/xsi.xml +++ /dev/null @@ -1,1481 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- cgit v1.2.3 From 67307e72eac2dcd3864b5a51072eae58dff51a5b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Mar 2020 11:04:50 +1100 Subject: Cleanup: use more descriptive variable name for the data-mask Make it explicit this data mask is added to the default mask. --- source/blender/blenkernel/BKE_mesh.h | 2 +- source/blender/blenkernel/intern/crazyspace.c | 8 ++++---- source/blender/blenkernel/intern/mesh.c | 4 ++-- source/blender/modifiers/intern/MOD_triangulate.c | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index ae2f5231e25..f14b9a30d99 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -87,7 +87,7 @@ struct Mesh *BKE_mesh_from_bmesh_for_eval_nomain(struct BMesh *bm, struct Mesh *BKE_mesh_from_editmesh_with_coords_thin_wrap( struct BMEditMesh *em, - const struct CustomData_MeshMasks *data_mask, + const struct CustomData_MeshMasks *cd_mask_extra, float (*vertexCos)[3], const struct Mesh *me_settings); diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c index bdca888efcc..ac62bd52011 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.c @@ -287,13 +287,13 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra if (mti->type == eModifierTypeType_OnlyDeform && mti->deformMatricesEM) { if (!defmats) { const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; - CustomData_MeshMasks data_mask = CD_MASK_BAREMESH; + CustomData_MeshMasks cd_mask_extra = CD_MASK_BAREMESH; CDMaskLink *datamasks = modifiers_calcDataMasks( - scene, ob, md, &data_mask, required_mode, NULL, NULL); - data_mask = datamasks->mask; + scene, ob, md, &cd_mask_extra, required_mode, NULL, NULL); + cd_mask_extra = datamasks->mask; BLI_linklist_free((LinkNode *)datamasks, NULL); - me = BKE_mesh_from_editmesh_with_coords_thin_wrap(em, &data_mask, NULL, me_input); + me = BKE_mesh_from_editmesh_with_coords_thin_wrap(em, &cd_mask_extra, NULL, me_input); deformedVerts = editbmesh_vert_coords_alloc(em, &numVerts); defmats = MEM_mallocN(sizeof(*defmats) * numVerts, "defmats"); diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 127acf0e464..3a9a38288a7 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -857,11 +857,11 @@ Mesh *BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, * TODO(campbell): support mesh with only an edit-mesh which is lazy initialized. */ Mesh *BKE_mesh_from_editmesh_with_coords_thin_wrap(BMEditMesh *em, - const CustomData_MeshMasks *data_mask, + const CustomData_MeshMasks *cd_mask_extra, float (*vertexCos)[3], const Mesh *me_settings) { - Mesh *me = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, data_mask, me_settings); + Mesh *me = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, cd_mask_extra, me_settings); /* Use editmesh directly where possible. */ me->runtime.is_original = true; if (vertexCos) { diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index 7fba7e864ae..94613353373 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -44,7 +44,7 @@ static Mesh *triangulate_mesh(Mesh *mesh, BMesh *bm; int total_edges, i; MEdge *me; - CustomData_MeshMasks cddata_masks = { + CustomData_MeshMasks cd_mask_extra = { .vmask = CD_MASK_ORIGINDEX, .emask = CD_MASK_ORIGINDEX, .pmask = CD_MASK_ORIGINDEX}; bool keep_clnors = (flag & MOD_TRIANGULATE_KEEP_CUSTOMLOOP_NORMALS) != 0; @@ -53,19 +53,19 @@ static Mesh *triangulate_mesh(Mesh *mesh, BKE_mesh_calc_normals_split(mesh); /* We need that one to 'survive' to/from BMesh conversions. */ CustomData_clear_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); - cddata_masks.lmask |= CD_MASK_NORMAL; + cd_mask_extra.lmask |= CD_MASK_NORMAL; } bm = BKE_mesh_to_bmesh_ex(mesh, &((struct BMeshCreateParams){0}), &((struct BMeshFromMeshParams){ .calc_face_normal = true, - .cd_mask_extra = cddata_masks, + .cd_mask_extra = cd_mask_extra, })); BM_mesh_triangulate(bm, quad_method, ngon_method, min_vertices, false, NULL, NULL, NULL); - result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cddata_masks, mesh); + result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh); BM_mesh_free(bm); if (keep_clnors) { -- cgit v1.2.3 From ab430cfdfe1c8214f1096d23e102cba706b59adc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Mar 2020 11:29:54 +1100 Subject: Cleanup: sort DNA renaming --- source/blender/makesdna/intern/dna_rename_defs.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 678baef3cef..9a6986c305a 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -64,6 +64,8 @@ DNA_STRUCT_RENAME_ELEM(Bone, curveOutX, curve_out_x) DNA_STRUCT_RENAME_ELEM(Bone, curveOutY, curve_out_y) DNA_STRUCT_RENAME_ELEM(Bone, scaleIn, scale_in_x) DNA_STRUCT_RENAME_ELEM(Bone, scaleOut, scale_out_x) +DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_f, hardeness) +DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_s, aspect_ratio) DNA_STRUCT_RENAME_ELEM(Camera, YF_dofdist, dof_distance) DNA_STRUCT_RENAME_ELEM(Camera, clipend, clip_end) DNA_STRUCT_RENAME_ELEM(Camera, clipsta, clip_start) @@ -83,8 +85,11 @@ DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) DNA_STRUCT_RENAME_ELEM(ThemeSpace, scrubbing_background, time_scrub_background) +DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) DNA_STRUCT_RENAME_ELEM(View3D, far, clip_end) DNA_STRUCT_RENAME_ELEM(View3D, near, clip_start) +DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_f, hardeness) +DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_s, aspect_ratio) DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveInX, curve_in_x) DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveInY, curve_in_y) DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveOutX, curve_out_x) @@ -109,8 +114,3 @@ DNA_STRUCT_RENAME_ELEM(bTheme, tstatusbar, space_statusbar) DNA_STRUCT_RENAME_ELEM(bTheme, ttopbar, space_topbar) DNA_STRUCT_RENAME_ELEM(bTheme, tuserpref, space_preferences) DNA_STRUCT_RENAME_ELEM(bTheme, tv3d, space_view3d) -DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) -DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_f, hardeness) -DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_s, aspect_ratio) -DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_f, hardeness) -DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_s, aspect_ratio) -- cgit v1.2.3 From b87997f59b41471f7f79d9071846b2145589d9b2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Mar 2020 11:32:03 +1100 Subject: Cleanup: rename 'centre' to 'center' in View3D --- source/blender/blenkernel/intern/lib_query.c | 2 +- source/blender/blenloader/intern/readfile.c | 4 ++-- source/blender/editors/armature/armature_naming.c | 6 +++--- source/blender/editors/gpencil/gpencil_primitive.c | 2 +- source/blender/editors/space_view3d/space_view3d.c | 6 +++--- source/blender/editors/space_view3d/view3d_edit.c | 8 ++++---- source/blender/editors/space_view3d/view3d_utils.c | 9 +++++---- source/blender/editors/space_view3d/view3d_view.c | 10 +++++----- source/blender/editors/transform/transform_convert.c | 2 +- source/blender/makesdna/DNA_view3d_types.h | 6 +++--- source/blender/makesdna/intern/dna_rename_defs.h | 3 +++ source/blender/makesrna/intern/rna_space.c | 6 +++--- 12 files changed, 34 insertions(+), 30 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 41d938e897d..c204c272de1 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -401,7 +401,7 @@ static void library_foreach_screen_area(LibraryForeachIDData *data, ScrArea *are View3D *v3d = (View3D *)sl; FOREACH_CALLBACK_INVOKE(data, v3d->camera, IDWALK_CB_NOP); - FOREACH_CALLBACK_INVOKE(data, v3d->ob_centre, IDWALK_CB_NOP); + FOREACH_CALLBACK_INVOKE(data, v3d->ob_center, IDWALK_CB_NOP); if (v3d->localvd) { FOREACH_CALLBACK_INVOKE(data, v3d->localvd->camera, IDWALK_CB_NOP); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 322abc2ec60..b8c79b3f064 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -7386,7 +7386,7 @@ static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area) View3D *v3d = (View3D *)sl; v3d->camera = newlibadr(fd, parent_id->lib, v3d->camera); - v3d->ob_centre = newlibadr(fd, parent_id->lib, v3d->ob_centre); + v3d->ob_center = newlibadr(fd, parent_id->lib, v3d->ob_center); if (v3d->localvd) { v3d->localvd->camera = newlibadr(fd, parent_id->lib, v3d->localvd->camera); @@ -7897,7 +7897,7 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, ARegion *region; v3d->camera = restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL); - v3d->ob_centre = restore_pointer_by_name(id_map, (ID *)v3d->ob_centre, USER_REAL); + v3d->ob_center = restore_pointer_by_name(id_map, (ID *)v3d->ob_center, USER_REAL); /* Free render engines for now. */ ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 1f421d23b27..bad4edbabaf 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -359,9 +359,9 @@ void ED_armature_bone_rename(Main *bmain, for (sl = sa->spacedata.first; sl; sl = sl->next) { if (sl->spacetype == SPACE_VIEW3D) { View3D *v3d = (View3D *)sl; - if (v3d->ob_centre && v3d->ob_centre->data == arm) { - if (STREQ(v3d->ob_centre_bone, oldname)) { - BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME); + if (v3d->ob_center && v3d->ob_center->data == arm) { + if (STREQ(v3d->ob_center_bone, oldname)) { + BLI_strncpy(v3d->ob_center_bone, newname, MAXBONENAME); } } } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 66c0131cd61..5cb34b1c08b 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -202,7 +202,7 @@ static void gp_rotate_v2_v2v2fl(float v[2], add_v2_v2v2(v, r, origin); } -/* Helper to rotate line around line centre */ +/* Helper to rotate line around line center. */ static void gp_primitive_rotate_line( float va[2], float vb[2], const float a[2], const float b[2], const float angle) { diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 020c58270fc..b2fc6c6e4cc 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1523,12 +1523,12 @@ static void view3d_id_remap(ScrArea *sa, SpaceLink *slink, ID *old_id, ID *new_i /* Values in local-view aren't used, see: T52663 */ if (is_local == false) { - if ((ID *)v3d->ob_centre == old_id) { - v3d->ob_centre = (Object *)new_id; + if ((ID *)v3d->ob_center == old_id) { + v3d->ob_center = (Object *)new_id; /* Otherwise, bonename may remain valid... * We could be smart and check this, too? */ if (new_id == NULL) { - v3d->ob_centre_bone[0] = '\0'; + v3d->ob_center_bone[0] = '\0'; } } } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 13b38a04f63..a7136cc580c 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -3182,7 +3182,7 @@ static int view_lock_to_active_exec(bContext *C, wmOperator *UNUSED(op)) if (v3d) { ED_view3d_lock_clear(v3d); - v3d->ob_centre = obact; /* can be NULL */ + v3d->ob_center = obact; /* can be NULL */ if (obact && obact->type == OB_ARMATURE) { if (obact->mode & OB_MODE_POSE) { @@ -3190,13 +3190,13 @@ static int view_lock_to_active_exec(bContext *C, wmOperator *UNUSED(op)) Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact); bPoseChannel *pcham_act = BKE_pose_channel_active(obact_eval); if (pcham_act) { - BLI_strncpy(v3d->ob_centre_bone, pcham_act->name, sizeof(v3d->ob_centre_bone)); + BLI_strncpy(v3d->ob_center_bone, pcham_act->name, sizeof(v3d->ob_center_bone)); } } else { EditBone *ebone_act = ((bArmature *)obact->data)->act_edbone; if (ebone_act) { - BLI_strncpy(v3d->ob_centre_bone, ebone_act->name, sizeof(v3d->ob_centre_bone)); + BLI_strncpy(v3d->ob_center_bone, ebone_act->name, sizeof(v3d->ob_center_bone)); } } } @@ -5140,7 +5140,7 @@ void ED_view3d_cursor3d_update(bContext *C, } /* offset the cursor lock to avoid jumping to new offset */ - if (v3d->ob_centre_cursor) { + if (v3d->ob_center_cursor) { if (U.uiflag & USER_LOCK_CURSOR_ADJUST) { float co_2d_curr[2], co_2d_prev[2]; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index e7153ddd361..bad283740c8 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -398,7 +398,7 @@ bool ED_view3d_boundbox_clip(RegionView3D *rv3d, const BoundBox *bb) bool ED_view3d_offset_lock_check(const View3D *v3d, const RegionView3D *rv3d) { - return (rv3d->persp != RV3D_CAMOB) && (v3d->ob_centre_cursor || v3d->ob_centre); + return (rv3d->persp != RV3D_CAMOB) && (v3d->ob_center_cursor || v3d->ob_center); } /** @@ -416,9 +416,10 @@ void ED_view3d_lastview_store(RegionView3D *rv3d) void ED_view3d_lock_clear(View3D *v3d) { - v3d->ob_centre = NULL; - v3d->ob_centre_bone[0] = '\0'; - v3d->ob_centre_cursor = false; + v3d->ob_center = NULL; + v3d->ob_center_bone[0] = '\0'; + v3d->ob_center_cursor = false; + v3d->flag2 &= ~V3D_LOCK_CAMERA; } diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index ee41b73ed16..b27bdf93389 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -834,13 +834,13 @@ void view3d_viewmatrix_set(Depsgraph *depsgraph, if (rv3d->persp == RV3D_PERSP) { rv3d->viewmat[3][2] -= rv3d->dist; } - if (v3d->ob_centre) { - Object *ob_eval = DEG_get_evaluated_object(depsgraph, v3d->ob_centre); + if (v3d->ob_center) { + Object *ob_eval = DEG_get_evaluated_object(depsgraph, v3d->ob_center); float vec[3]; copy_v3_v3(vec, ob_eval->obmat[3]); - if (ob_eval->type == OB_ARMATURE && v3d->ob_centre_bone[0]) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob_eval->pose, v3d->ob_centre_bone); + if (ob_eval->type == OB_ARMATURE && v3d->ob_center_bone[0]) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_eval->pose, v3d->ob_center_bone); if (pchan) { copy_v3_v3(vec, pchan->pose_mat[3]); mul_m4_v3(ob_eval->obmat, vec); @@ -849,7 +849,7 @@ void view3d_viewmatrix_set(Depsgraph *depsgraph, translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]); use_lock_ofs = true; } - else if (v3d->ob_centre_cursor) { + else if (v3d->ob_center_cursor) { float vec[3]; copy_v3_v3(vec, scene->cursor.location); translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]); diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 4ccb97b7a00..0a2e05e6f49 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -2760,7 +2760,7 @@ void createTransData(bContext *C, TransInfo *t) t->flag |= T_CAMERA; } } - else if (v3d->ob_centre && v3d->ob_centre->id.tag & LIB_TAG_DOIT) { + else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) { t->flag |= T_CAMERA; } } diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index edc86a4b0ea..a25d66cbc9b 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -265,14 +265,14 @@ typedef struct View3D { short persp DNA_DEPRECATED; short view DNA_DEPRECATED; - struct Object *camera, *ob_centre; + struct Object *camera, *ob_center; rctf render_border; /** Allocated backup of its self while in localview. */ struct View3D *localvd; /** Optional string for armature bone to define center, MAXBONENAME. */ - char ob_centre_bone[64]; + char ob_center_bone[64]; unsigned short local_view_uuid; char _pad6[2]; @@ -281,7 +281,7 @@ typedef struct View3D { short _pad7[3]; /** Optional bool for 3d cursor to define center. */ - short ob_centre_cursor; + short ob_center_cursor; short scenelock; short gp_flag; short flag; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 9a6986c305a..aabc16c4111 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -88,6 +88,9 @@ DNA_STRUCT_RENAME_ELEM(ThemeSpace, scrubbing_background, time_scrub_background) DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) DNA_STRUCT_RENAME_ELEM(View3D, far, clip_end) DNA_STRUCT_RENAME_ELEM(View3D, near, clip_start) +DNA_STRUCT_RENAME_ELEM(View3D, ob_centre, ob_center) +DNA_STRUCT_RENAME_ELEM(View3D, ob_centre_bone, ob_center_bone) +DNA_STRUCT_RENAME_ELEM(View3D, ob_centre_cursor, ob_center_cursor) DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_f, hardeness) DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_s, aspect_ratio) DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveInX, curve_in_x) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 89e1babde7d..1cd499520d9 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3958,19 +3958,19 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_object", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_pointer_sdna(prop, NULL, "ob_centre"); + RNA_def_property_pointer_sdna(prop, NULL, "ob_center"); RNA_def_property_ui_text( prop, "Lock to Object", "3D View center is locked to this object's position"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); prop = RNA_def_property(srna, "lock_bone", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "ob_centre_bone"); + RNA_def_property_string_sdna(prop, NULL, "ob_center_bone"); RNA_def_property_ui_text( prop, "Lock to Bone", "3D View center is locked to this bone's position"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); prop = RNA_def_property(srna, "lock_cursor", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "ob_centre_cursor", 1); + RNA_def_property_boolean_sdna(prop, NULL, "ob_center_cursor", 1); RNA_def_property_ui_text( prop, "Lock to Cursor", "3D View center is locked to the cursor's position"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); -- cgit v1.2.3 From 90ce708ef0b086c4cd148996cb039cd7ece21457 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 17 Mar 2020 11:13:14 +0100 Subject: BKE_lib_id: Add helper to swap full ID content and use proper naming. Preliminary work for undo-speedup. Part of T60695/D6580. --- source/blender/blenkernel/BKE_lib_id.h | 5 ++- source/blender/blenkernel/intern/lib_id.c | 48 +++++++++++++++++++------ source/blender/blenkernel/intern/lib_override.c | 2 +- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 27dd1e637f3..25ccc5bd5b6 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -217,7 +217,10 @@ bool id_single_user(struct bContext *C, bool BKE_id_copy_is_allowed(const struct ID *id); bool BKE_id_copy(struct Main *bmain, const struct ID *id, struct ID **newid); bool BKE_id_copy_ex(struct Main *bmain, const struct ID *id, struct ID **r_newid, const int flag); -void BKE_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b); + +void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b); +void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); + void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 27d4b8dd047..1ae53f5a85d 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -597,7 +597,7 @@ bool BKE_id_copy(Main *bmain, const ID *id, ID **newid) * Does a mere memory swap over the whole IDs data (including type-specific memory). * \note Most internal ID data itself is not swapped (only IDProperties are). */ -void BKE_id_swap(Main *bmain, ID *id_a, ID *id_b) +static void id_swap(Main *bmain, ID *id_a, ID *id_b, const bool do_full_id) { BLI_assert(GS(id_a->name) == GS(id_b->name)); @@ -651,17 +651,45 @@ void BKE_id_swap(Main *bmain, ID *id_a, ID *id_b) #undef CASE_SWAP - /* Restore original ID's internal data. */ - *id_a = id_a_back; - *id_b = id_b_back; + if (!do_full_id) { + /* Restore original ID's internal data. */ + *id_a = id_a_back; + *id_b = id_b_back; - /* Exception: IDProperties. */ - id_a->properties = id_b_back.properties; - id_b->properties = id_a_back.properties; + /* Exception: IDProperties. */ + id_a->properties = id_b_back.properties; + id_b->properties = id_a_back.properties; + } - /* Swap will have broken internal references to itself, restore them. */ - BKE_libblock_relink_ex(bmain, id_a, id_b, id_a, ID_REMAP_SKIP_NEVER_NULL_USAGE); - BKE_libblock_relink_ex(bmain, id_b, id_a, id_b, ID_REMAP_SKIP_NEVER_NULL_USAGE); + if (bmain != NULL) { + /* Swap will have broken internal references to itself, restore them. */ + BKE_libblock_relink_ex(bmain, id_a, id_b, id_a, ID_REMAP_SKIP_NEVER_NULL_USAGE); + BKE_libblock_relink_ex(bmain, id_b, id_a, id_b, ID_REMAP_SKIP_NEVER_NULL_USAGE); + } +} + +/** + * Does a mere memory swap over the whole IDs data (including type-specific memory). + * \note Most internal ID data itself is not swapped (only IDProperties are). + * + * \param bmain May be NULL, in which case there will be no remapping of internal pointers to + * itself. + */ +void BKE_lib_id_swap(Main *bmain, ID *id_a, ID *id_b) +{ + id_swap(bmain, id_a, id_b, false); +} + +/** + * Does a mere memory swap over the whole IDs data (including type-specific memory). + * \note All internal ID data itself is also swapped. + * + * \param bmain May be NULL, in which case there will be no remapping of internal pointers to + * itself. + */ +void BKE_lib_id_swap_full(Main *bmain, ID *id_a, ID *id_b) +{ + id_swap(bmain, id_a, id_b, true); } /** Does *not* set ID->newid pointer. */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index af861b5b0e9..81f7e158b40 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -813,7 +813,7 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) /* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa. * So when we'll free tmp_id, we'll actually free old, outdated data from local. */ - BKE_id_swap(bmain, local, tmp_id); + BKE_lib_id_swap(bmain, local, tmp_id); /* Again, horribly inn-efficient in our case, we need something off-Main * (aka more generic nolib copy/free stuff)! */ -- cgit v1.2.3 From 2ba3e6a02e5c15b5532d82e64cc6214149272e8e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 17 Mar 2020 11:24:37 +0100 Subject: Depsgraph: Adds helpers to extract/restore despgraphs in a given Main. Extract will steal all depsgraphs currently stored in given bmain, and restore will put them back in place, using scene and viewlayers as keys. Preliminary work for undo-speedup. Part of T60695/D6580. --- source/blender/blenkernel/BKE_scene.h | 4 + source/blender/blenkernel/intern/scene.c | 128 +++++++++++++++++++++++---- source/blender/depsgraph/DEG_depsgraph.h | 5 ++ source/blender/depsgraph/intern/depsgraph.cc | 23 +++++ 4 files changed, 144 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index f78c7b66bb4..c10f8d39bb2 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -30,6 +30,7 @@ extern "C" { struct AviCodecData; struct Collection; struct Depsgraph; +struct GHash; struct Main; struct Object; struct RenderData; @@ -222,6 +223,9 @@ struct Depsgraph *BKE_scene_get_depsgraph(struct Main *bmain, struct ViewLayer *view_layer, bool allocate); +struct GHash *BKE_scene_undo_depsgraphs_extract(struct Main *bmain); +void BKE_scene_undo_depsgraphs_restore(struct Main *bmain, struct GHash *depsgraph_extract); + void BKE_scene_transform_orientation_remove(struct Scene *scene, struct TransformOrientation *orientation); struct TransformOrientation *BKE_scene_transform_orientation_find(const struct Scene *scene, diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 98638b6c865..ff0232ed9aa 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2067,12 +2067,16 @@ void BKE_scene_free_view_layer_depsgraph(Scene *scene, ViewLayer *view_layer) /* Query depsgraph for a specific contexts. */ -Depsgraph *BKE_scene_get_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, bool allocate) +static Depsgraph **scene_get_depsgraph_p(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + const bool allocate_ghash_entry, + const bool allocate_depsgraph) { BLI_assert(scene != NULL); BLI_assert(view_layer != NULL); /* Make sure hash itself exists. */ - if (allocate) { + if (allocate_ghash_entry) { BKE_scene_ensure_depsgraph_hash(scene); } if (scene->depsgraph_hash == NULL) { @@ -2083,29 +2087,121 @@ Depsgraph *BKE_scene_get_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_la */ DepsgraphKey key; key.view_layer = view_layer; - Depsgraph *depsgraph; - if (allocate) { + Depsgraph **depsgraph_ptr; + if (allocate_ghash_entry) { DepsgraphKey **key_ptr; - Depsgraph **depsgraph_ptr; if (!BLI_ghash_ensure_p_ex( scene->depsgraph_hash, &key, (void ***)&key_ptr, (void ***)&depsgraph_ptr)) { *key_ptr = MEM_mallocN(sizeof(DepsgraphKey), __func__); **key_ptr = key; - *depsgraph_ptr = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT); - /* TODO(sergey): Would be cool to avoid string format print, - * but is a bit tricky because we can't know in advance whether - * we will ever enable debug messages for this depsgraph. - */ - char name[1024]; - BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name); - DEG_debug_name_set(*depsgraph_ptr, name); + if (allocate_depsgraph) { + *depsgraph_ptr = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT); + /* TODO(sergey): Would be cool to avoid string format print, + * but is a bit tricky because we can't know in advance whether + * we will ever enable debug messages for this depsgraph. + */ + char name[1024]; + BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name); + DEG_debug_name_set(*depsgraph_ptr, name); + } + else { + *depsgraph_ptr = NULL; + } } - depsgraph = *depsgraph_ptr; } else { - depsgraph = BLI_ghash_lookup(scene->depsgraph_hash, &key); + depsgraph_ptr = (Depsgraph **)BLI_ghash_lookup_p(scene->depsgraph_hash, &key); + } + return depsgraph_ptr; +} + +Depsgraph *BKE_scene_get_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, bool allocate) +{ + Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(bmain, scene, view_layer, allocate, allocate); + return (depsgraph_ptr != NULL) ? *depsgraph_ptr : NULL; +} + +static char *scene_undo_depsgraph_gen_key(Scene *scene, ViewLayer *view_layer, char *key_full) +{ + if (key_full == NULL) { + key_full = MEM_callocN(MAX_ID_NAME + FILE_MAX + MAX_NAME, __func__); + } + + size_t key_full_offset = BLI_strncpy_rlen(key_full, scene->id.name, MAX_ID_NAME); + if (scene->id.lib != NULL) { + key_full_offset += BLI_strncpy_rlen(key_full + key_full_offset, scene->id.lib->name, FILE_MAX); + } + key_full_offset += BLI_strncpy_rlen(key_full + key_full_offset, view_layer->name, MAX_NAME); + BLI_assert(key_full_offset < MAX_ID_NAME + FILE_MAX + MAX_NAME); + + return key_full; +} + +GHash *BKE_scene_undo_depsgraphs_extract(Main *bmain) +{ + GHash *depsgraph_extract = BLI_ghash_new( + BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); + + for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { + if (scene->depsgraph_hash == NULL) { + /* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will be + * built so this pointer may be NULL. */ + continue; + } + for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL; + view_layer = view_layer->next) { + DepsgraphKey key; + key.view_layer = view_layer; + Depsgraph **depsgraph = (Depsgraph **)BLI_ghash_lookup_p(scene->depsgraph_hash, &key); + + if (depsgraph != NULL && *depsgraph != NULL) { + char *key_full = scene_undo_depsgraph_gen_key(scene, view_layer, NULL); + + /* We steal the depsgraph from the scene. */ + BLI_ghash_insert(depsgraph_extract, key_full, *depsgraph); + *depsgraph = NULL; + } + } + } + + return depsgraph_extract; +} + +void BKE_scene_undo_depsgraphs_restore(Main *bmain, GHash *depsgraph_extract) +{ + for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { + BLI_assert(scene->depsgraph_hash == NULL); + + for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL; + view_layer = view_layer->next) { + char key_full[MAX_ID_NAME + FILE_MAX + MAX_NAME] = {0}; + scene_undo_depsgraph_gen_key(scene, view_layer, key_full); + + Depsgraph **depsgraph_extract_ptr = (Depsgraph **)BLI_ghash_lookup_p(depsgraph_extract, + key_full); + if (depsgraph_extract_ptr == NULL) { + continue; + } + BLI_assert(*depsgraph_extract_ptr != NULL); + + Depsgraph **depsgraph_scene_ptr = scene_get_depsgraph_p( + bmain, scene, view_layer, true, false); + BLI_assert(depsgraph_scene_ptr != NULL); + BLI_assert(*depsgraph_scene_ptr == NULL); + + /* We steal the depsgraph back from our 'extract' storage to the scene. */ + Depsgraph *depsgraph = *depsgraph_extract_ptr; + + DEG_graph_replace_owners(depsgraph, bmain, scene, view_layer); + + DEG_graph_tag_relations_update(depsgraph); + + *depsgraph_scene_ptr = depsgraph; + *depsgraph_extract_ptr = NULL; + } } - return depsgraph; + + BLI_ghash_free(depsgraph_extract, MEM_freeN, depsgraph_key_value_free); } /* -------------------------------------------------------------------- */ diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index d5a93d21b99..c94a8876ab0 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -92,6 +92,11 @@ Depsgraph *DEG_graph_new(struct Main *bmain, struct ViewLayer *view_layer, eEvaluationMode mode); +void DEG_graph_replace_owners(struct Depsgraph *depsgraph, + struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer); + /* Free Depsgraph itself and all its data */ void DEG_graph_free(Depsgraph *graph); diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index ce6797939b5..a19a2958f28 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -285,6 +285,29 @@ Depsgraph *DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEval return reinterpret_cast(deg_depsgraph); } +/* Replace the "owner" pointers (currently Main/Scene/ViewLayer) of this depsgraph. + * Used during undo steps when we do want to re-use the old depsgraph data as much as possible. */ +void DEG_graph_replace_owners(struct Depsgraph *depsgraph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) +{ + DEG::Depsgraph *deg_graph = reinterpret_cast(depsgraph); + + const bool do_update_register = deg_graph->bmain != bmain; + if (do_update_register && deg_graph->bmain != NULL) { + DEG::unregister_graph(deg_graph); + } + + deg_graph->bmain = bmain; + deg_graph->scene = scene; + deg_graph->view_layer = view_layer; + + if (do_update_register) { + DEG::register_graph(deg_graph); + } +} + /* Free graph's contents and graph itself */ void DEG_graph_free(Depsgraph *graph) { -- cgit v1.2.3 From 7f3e84deb5aff74106c29965b70664cdb9883ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Barschkis?= Date: Mon, 16 Mar 2020 17:08:06 +0100 Subject: Fluid: Updated manta pp files Includes only a rename. The name PyInit_Main was a bit confusing as it just belongs to Manta. --- extern/mantaflow/helper/pwrapper/registry.cpp | 4 ++-- extern/mantaflow/helper/pwrapper/registry.h | 2 +- extern/mantaflow/preprocessed/gitinfo.h | 2 +- intern/mantaflow/intern/manta_python_API.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extern/mantaflow/helper/pwrapper/registry.cpp b/extern/mantaflow/helper/pwrapper/registry.cpp index 3cdc2248b98..5b313e52fb4 100644 --- a/extern/mantaflow/helper/pwrapper/registry.cpp +++ b/extern/mantaflow/helper/pwrapper/registry.cpp @@ -192,7 +192,7 @@ int cbDisableConstructor(PyObject *self, PyObject *args, PyObject *kwds) return -1; } -PyMODINIT_FUNC PyInit_Main(void) +PyMODINIT_FUNC PyInit_manta_main(void) { MantaEnsureRegistration(); #if PY_MAJOR_VERSION >= 3 @@ -567,7 +567,7 @@ void WrapperRegistry::construct(const string &scriptname, const vector & registerDummyTypes(); // work around for certain gcc versions, cast to char* - PyImport_AppendInittab((char *)gDefaultModuleName.c_str(), PyInit_Main); + PyImport_AppendInittab((char *)gDefaultModuleName.c_str(), PyInit_manta_main); } inline PyObject *castPy(PyTypeObject *p) diff --git a/extern/mantaflow/helper/pwrapper/registry.h b/extern/mantaflow/helper/pwrapper/registry.h index 139863df85d..d9d2bbb624b 100644 --- a/extern/mantaflow/helper/pwrapper/registry.h +++ b/extern/mantaflow/helper/pwrapper/registry.h @@ -62,7 +62,7 @@ void MantaEnsureRegistration(); #ifdef BLENDER # ifdef PyMODINIT_FUNC -PyMODINIT_FUNC PyInit_Main(void); +PyMODINIT_FUNC PyInit_manta_main(void); # endif #endif diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h index 574b655d6ce..208d8008a7e 100644 --- a/extern/mantaflow/preprocessed/gitinfo.h +++ b/extern/mantaflow/preprocessed/gitinfo.h @@ -1,3 +1,3 @@ -#define MANTA_GIT_VERSION "commit caae2ddea6ea895677aca88df00a130a67eeb6c7" +#define MANTA_GIT_VERSION "commit 5fbd3d04381b21afce4a593d1fe2d9bc7bef5424" diff --git a/intern/mantaflow/intern/manta_python_API.cpp b/intern/mantaflow/intern/manta_python_API.cpp index 8f7a396410a..a905e045fd3 100644 --- a/intern/mantaflow/intern/manta_python_API.cpp +++ b/intern/mantaflow/intern/manta_python_API.cpp @@ -26,5 +26,5 @@ PyObject *Manta_initPython(void) { - return Pb::PyInit_Main(); + return Pb::PyInit_manta_main(); } -- cgit v1.2.3 From 20d7c04305a39bc18d483baba26dfb54960bb92a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Barschkis?= Date: Mon, 16 Mar 2020 19:10:25 +0100 Subject: Fluid: Re-dded Empty Space option in the UI This option existed already and was just hidden in the UI. With the new fluids system though, it will only be used for rendering - and not to optimize the cache. --- release/scripts/startup/bl_ui/properties_physics_fluid.py | 3 +-- source/blender/blenkernel/intern/fluid.c | 2 +- source/blender/makesrna/intern/rna_fluid.c | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index ba21fe47519..65992a4e4e9 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -192,8 +192,7 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): sub.prop(domain, "gravity", text="Using Scene Gravity", icon='SCENE_DATA') else: col.prop(domain, "gravity", text="Gravity") - # TODO (sebbas): Clipping var useful for manta openvdb caching? - # col.prop(domain, "clipping", text="Empty Space") + col.prop(domain, "clipping", text="Empty Space") if domain.cache_type == 'MODULAR': col.separator() diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 366137b5fa6..33124bd23a5 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -4851,7 +4851,7 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *mmd) #else mmd->domain->openvdb_comp = VDB_COMPRESSION_ZIP; #endif - mmd->domain->clipping = 1e-3f; + mmd->domain->clipping = 1e-6f; mmd->domain->data_depth = 0; } else if (mmd->type & MOD_FLUID_TYPE_FLOW) { diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 7bd353cd441..b4543ea4b38 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -2209,11 +2209,11 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "clipping", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "clipping"); RNA_def_property_range(prop, 0.0, 1.0); - RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 3); + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 6); RNA_def_property_ui_text( prop, "Clipping", - "Value under which voxels are considered empty space to optimize caching and rendering"); + "Value under which voxels are considered empty space to optimize rendering"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL); /* -- Deprecated / unsed options (below)-- */ -- cgit v1.2.3 From 055863d9c1f0195765f421b1f87e445dd070ea00 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 17 Mar 2020 11:32:52 +0100 Subject: Cleanup: minor changes. --- source/blender/blenkernel/intern/undo_system.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index 74b322aeab7..30867cc15e9 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -163,6 +163,7 @@ static bool undosys_step_encode(bContext *C, Main *bmain, UndoStack *ustack, Und * not all members are filled in. */ us->type->step_foreach_ID_ref(us, undosys_id_ref_store, bmain); } + #ifdef WITH_GLOBAL_UNDO_CORRECT_ORDER if (us->type == BKE_UNDOSYS_TYPE_MEMFILE) { ustack->step_active_memfile = us; @@ -193,7 +194,7 @@ static void undosys_step_decode( * undo step will be correctly resolved, see: T56163. */ undosys_step_decode(C, bmain, ustack, us_iter, dir, false); /* May have been freed on memfile read. */ - bmain = G.main; + bmain = G_MAIN; } break; } -- cgit v1.2.3 From b02a25b7e10e5713052f5a6ccf4127ad4d995f25 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 17 Mar 2020 12:09:06 +0100 Subject: Add accumulated recalc flags to IDs. Those accumulated flags get cleared every time an undo step is written to memfile. Preliminary work for undo-speedup. Part of T60695/D6580. --- source/blender/blenloader/intern/writefile.c | 17 ++++++++++++++++- source/blender/depsgraph/intern/depsgraph_tag.cc | 4 ++++ source/blender/makesdna/DNA_ID.h | 8 ++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index a92404b2372..1e50cda3eaf 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -703,7 +703,7 @@ void IDP_WriteProperty(const IDProperty *prop, void *wd) IDP_WriteProperty_OnlyData(prop, wd); } -static void write_iddata(void *wd, const ID *id) +static void write_iddata(WriteData *wd, ID *id) { /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */ if (id->properties && !ELEM(GS(id->name), ID_WM)) { @@ -731,6 +731,11 @@ static void write_iddata(void *wd, const ID *id) } } } + + /* Clear the accumulated recalc flags in case of undo step saving. */ + if (wd->use_memfile) { + id->recalc_undo_accumulated = 0; + } } static void write_previews(WriteData *wd, const PreviewImage *prv_orig) @@ -1131,6 +1136,11 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree) for (sock = ntree->outputs.first; sock; sock = sock->next) { write_node_socket_interface(wd, sock); } + + /* Clear the accumulated recalc flags in case of undo step saving. */ + if (wd->use_memfile) { + ntree->id.recalc_undo_accumulated = 0; + } } /** @@ -2425,6 +2435,11 @@ static void write_collection_nolib(WriteData *wd, Collection *collection) for (CollectionChild *child = collection->children.first; child; child = child->next) { writestruct(wd, DATA, CollectionChild, 1, child); } + + /* Clear the accumulated recalc flags in case of undo step saving. */ + if (wd->use_memfile) { + collection->id.recalc_undo_accumulated = 0; + } } static void write_collection(WriteData *wd, Collection *collection) diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index b019c079dab..8decb9f6b87 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -814,12 +814,16 @@ void DEG_ids_check_recalc( static void deg_graph_clear_id_recalc_flags(ID *id) { + /* Keep incremental track of used recalc flags, to get proper update when undoing. */ + id->recalc_undo_accumulated |= id->recalc; id->recalc &= ~ID_RECALC_ALL; bNodeTree *ntree = ntreeFromID(id); /* Clear embedded node trees too. */ if (ntree) { + ntree->id.recalc_undo_accumulated |= ntree->id.recalc; ntree->id.recalc &= ~ID_RECALC_ALL; } + /* XXX And what about scene's master collection here? */ } void DEG_ids_clear_recalc(Main *UNUSED(bmain), Depsgraph *depsgraph) diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 40916cbdc61..f2923c7f144 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -245,6 +245,12 @@ typedef struct ID { int us; int icon_id; int recalc; + /** + * Used by undo code. Value of recalc is stored there when reading an ID from memfile, and not + * touched by anything, which means it can be used as 'reference' recalc value for the next undo + * step, when going backward (i.e. actual undo, redo can just use recalc value directly). + */ + int recalc_undo_accumulated; /** * A session-wide unique identifier for a given ID, that remain the same across potential @@ -252,6 +258,8 @@ typedef struct ID { */ unsigned int session_uuid; + char _pad[4]; + IDProperty *properties; /** Reference linked ID which this one overrides. */ -- cgit v1.2.3 From 0b7841679ee846e6d69f5f99e41fb92c1b3a8389 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 11:39:42 +0100 Subject: Multires: Cleanup, naming, make it more consistent The coarse mesh is an input to generic Subdiv, and exact meaning is ambiguous. The input to Multires is a base mesh, which owns CD_MDISPS. --- source/blender/blenkernel/intern/multires_reshape_util.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index f9271e37672..a916a8b87c6 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -53,21 +53,21 @@ Subdiv *multires_reshape_create_subdiv(Depsgraph *depsgraph, /*const*/ Object *object, const MultiresModifierData *mmd) { - Mesh *coarse_mesh; + Mesh *base_mesh; if (depsgraph != NULL) { Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - coarse_mesh = mesh_get_eval_deform(depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH); + base_mesh = mesh_get_eval_deform(depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH); } else { - coarse_mesh = (Mesh *)object->data; + base_mesh = (Mesh *)object->data; } SubdivSettings subdiv_settings; BKE_multires_subdiv_settings_init(&subdiv_settings, mmd); - Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, coarse_mesh); - if (!BKE_subdiv_eval_update_from_mesh(subdiv, coarse_mesh, NULL)) { + Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, base_mesh); + if (!BKE_subdiv_eval_update_from_mesh(subdiv, base_mesh, NULL)) { BKE_subdiv_free(subdiv); return NULL; } @@ -303,8 +303,8 @@ int multires_reshape_grid_to_corner(const MultiresReshapeContext *reshape_contex bool multires_reshape_is_quad_face(const MultiresReshapeContext *reshape_context, int face_index) { - const MPoly *coarse_poly = &reshape_context->base_mesh->mpoly[face_index]; - return (coarse_poly->totloop == 4); + const MPoly *base_poly = &reshape_context->base_mesh->mpoly[face_index]; + return (base_poly->totloop == 4); } /* For the given grid index get index of corresponding ptex face. */ -- cgit v1.2.3 From 54c877069245ab41f74c9d545b040355a2cf0937 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 16 Mar 2020 23:15:24 +0100 Subject: Fix error using CUDA in plug-ins on Linux/macOS, hide our CUDA symbols Better solution will be to hide all symbols by default, but this works for now. --- source/creator/blender.map | 1 + source/creator/osx_locals.map | 1 + 2 files changed, 2 insertions(+) diff --git a/source/creator/blender.map b/source/creator/blender.map index 1a9c995e684..4c34dea3338 100644 --- a/source/creator/blender.map +++ b/source/creator/blender.map @@ -21,5 +21,6 @@ local: *boost*; *SDL*; *embree*; + cu*; }; diff --git a/source/creator/osx_locals.map b/source/creator/osx_locals.map index f3a242f6896..3382ac954e2 100644 --- a/source/creator/osx_locals.map +++ b/source/creator/osx_locals.map @@ -6,4 +6,5 @@ *LLVM* *OSL* *embree* +cu* -- cgit v1.2.3 From b2851dbe784ea82238720a27a0942128fe8158c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Barschkis?= Date: Tue, 17 Mar 2020 13:49:47 +0100 Subject: Fluid: Abort baking jobs faster With this change baking jobs will be aborted faster. The user will not have to wait for the current frame to finish baking. The bake job will exit early and discard the incomplete frame. --- source/blender/blenkernel/intern/fluid.c | 53 +++++++++++++++++++------- source/blender/editors/physics/physics_fluid.c | 6 +++ 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 33124bd23a5..2a94b56b68c 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -39,6 +39,7 @@ #include "BKE_effect.h" #include "BKE_fluid.h" +#include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_modifier.h" #include "BKE_pointcache.h" @@ -3524,7 +3525,7 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obje return result; } -static void manta_step( +static int manta_step( Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me, FluidModifierData *mmd, int frame) { FluidDomainSettings *mds = mmd->domain; @@ -3532,17 +3533,20 @@ static void manta_step( float time_per_frame; bool init_resolution = true; - /* update object state */ + /* Store baking success - bake might be aborted anytime by user. */ + int result = 1; + + /* Update object state. */ invert_m4_m4(mds->imat, ob->obmat); copy_m4_m4(mds->obmat, ob->obmat); - /* gas domain might use adaptive domain */ + /* Gas domain might use adaptive domain. */ if (mds->type == FLUID_DOMAIN_TYPE_GAS) { init_resolution = (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) != 0; } manta_set_domain_from_mesh(mds, ob, me, init_resolution); - /* use local variables for adaptive loop, dt can change */ + /* Use local variables for adaptive loop, dt can change. */ frame_length = mds->frame_length; dt = mds->dt; time_per_frame = 0; @@ -3550,27 +3554,39 @@ static void manta_step( BLI_mutex_lock(&object_update_lock); - /* loop as long as time_per_frame (sum of sub dt's) does not exceed actual framelength */ + /* Loop as long as time_per_frame (sum of sub dt's) does not exceed actual framelength. */ while (time_per_frame < frame_length) { manta_adapt_timestep(mds->fluid); dt = manta_get_timestep(mds->fluid); - /* save adapted dt so that MANTA object can access it (important when adaptive domain creates - * new MANTA object) */ + /* Save adapted dt so that MANTA object can access it (important when adaptive domain creates + * new MANTA object). */ mds->dt = dt; - /* count for how long this while loop is running */ + /* Count for how long this while loop is running. */ time_per_frame += dt; time_total += dt; - /* Calculate inflow geometry */ + /* Calculate inflow geometry. */ update_flowsfluids(depsgraph, scene, ob, mds, time_per_frame, frame_length, frame, dt); + /* If user requested stop, quit baking */ + if (G.is_break) { + result = 0; + break; + } + manta_update_variables(mds->fluid, mmd); - /* Calculate obstacle geometry */ + /* Calculate obstacle geometry. */ update_obstacles(depsgraph, scene, ob, mds, time_per_frame, frame_length, frame, dt); + /* If user requested stop, quit baking */ + if (G.is_break) { + result = 0; + break; + } + if (mds->total_cells > 1) { update_effectors(depsgraph, scene, ob, mds, dt); manta_bake_data(mds->fluid, mmd, frame); @@ -3578,12 +3594,20 @@ static void manta_step( mds->time_per_frame = time_per_frame; mds->time_total = time_total; } + + /* If user requested stop, quit baking */ + if (G.is_break) { + result = 0; + break; + } } if (mds->type == FLUID_DOMAIN_TYPE_GAS) { manta_smoke_calc_transparency(mds, DEG_get_evaluated_view_layer(depsgraph)); } BLI_mutex_unlock(&object_update_lock); + + return result; } static void manta_guiding( @@ -3952,9 +3976,11 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, manta_guiding(depsgraph, scene, ob, mmd, scene_framenr); } if (baking_data) { - manta_step(depsgraph, scene, ob, me, mmd, scene_framenr); - manta_write_config(mds->fluid, mmd, scene_framenr); - manta_write_data(mds->fluid, mmd, scene_framenr); + /* Only save baked data if all of it completed successfully. */ + if (manta_step(depsgraph, scene, ob, me, mmd, scene_framenr)) { + manta_write_config(mds->fluid, mmd, scene_framenr); + manta_write_data(mds->fluid, mmd, scene_framenr); + } } if (has_data || baking_data) { if (baking_noise && with_smoke && with_noise) { @@ -3969,6 +3995,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, } } mmd->time = scene_framenr; + G.is_break = false; } static void BKE_fluid_modifier_process( diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index 147c765143e..2db5eb784c0 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -305,6 +305,12 @@ static void fluid_bake_sequence(FluidJob *job) /* Update animation system */ ED_update_for_newframe(job->bmain, job->depsgraph); + + /* If user requested stop, quit baking */ + if (G.is_break) { + job->success = 0; + return; + } } /* Restore frame position that we were on before bake */ -- cgit v1.2.3 From 3c1433b6f3b29e5c651dd7103bd6e44aef103fb5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Mar 2020 23:45:59 +1100 Subject: Revert "Cleanup: use doxy sections" This reverts commit 626b2bd071b334201996081b907e18d9c2dee919. Sergey prefers not to use doxy sections for this code. Revert pending a decision on T74845 --- .../blender/blenkernel/intern/multires_reshape.h | 80 ++++++++-------------- .../blenkernel/intern/multires_reshape_util.c | 51 +++++--------- 2 files changed, 49 insertions(+), 82 deletions(-) diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h index 9029d19ae88..bdadc1f2800 100644 --- a/source/blender/blenkernel/intern/multires_reshape.h +++ b/source/blender/blenkernel/intern/multires_reshape.h @@ -132,9 +132,9 @@ typedef struct ReshapeConstGridElement { float mask; } ReshapeConstGridElement; -/* -------------------------------------------------------------------- */ -/** \name Construct/destruct reshape context. - * \{ */ +/* ================================================================================================ + * Construct/destruct reshape context. + */ /* Create subdivision surface descriptor which is configured for surface evaluation at a given * multires modifier. */ @@ -159,11 +159,9 @@ bool multires_reshape_context_create_from_subdivide(MultiresReshapeContext *resh void multires_reshape_context_free(MultiresReshapeContext *reshape_context); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Helper accessors. - * \{ */ +/* ================================================================================================ + * Helper accessors. + */ /* For the given grid index get index of face it was created for. */ int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context, @@ -206,11 +204,9 @@ ReshapeGridElement multires_reshape_grid_element_for_ptex_coord( ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord( const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sample limit surface of the base mesh. - * \{ */ +/* ================================================================================================ + * Sample limit surface of the base mesh. + */ /* Evaluate limit surface created from base mesh. * This is the limit surface which defines tangent space for MDisps. */ @@ -219,20 +215,16 @@ void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *resha float r_P[3], float r_tangent_matrix[3][3]); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Custom data preparation. - * \{ */ +/* ================================================================================================ + * Custom data preparation. + */ /* Make sure custom data is allocated for the given level. */ void multires_reshape_ensure_grids(struct Mesh *mesh, const int level); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Functions specific to reshaping from a set of vertices in a object position. - * \{ */ +/* ================================================================================================ + * Functions specific to reshaping from a set of vertices in a object position. + */ /* Returns truth if all coordinates were assigned. * @@ -243,11 +235,9 @@ bool multires_reshape_assign_final_coords_from_vertcos( const float (*vert_coords)[3], const int num_vert_coords); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Functions specific to reshaping from CCG. - * \{ */ +/* ================================================================================================ + * Functions specific to reshaping from CCG. + */ /* NOTE: Displacement grids to be at least at a reshape level. * @@ -255,11 +245,9 @@ bool multires_reshape_assign_final_coords_from_vertcos( bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext *reshape_context, struct SubdivCCG *subdiv_ccg); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Functions specific to reshaping from MDISPS. - * \{ */ +/* ================================================================================================ + * Functions specific to reshaping from MDISPS. + */ /* Reads and writes to the current mesh CD_MDISPS. */ void multires_reshape_assign_final_coords_from_mdisps( @@ -269,11 +257,9 @@ void multires_reshape_assign_final_coords_from_mdisps( void multires_reshape_assign_final_coords_from_orig_mdisps( const MultiresReshapeContext *reshape_context); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Displacement smooth. - * \{ */ +/* ================================================================================================ + * Displacement smooth. + */ /* Operates on a displacement grids (CD_MDISPS) which contains object space coordinates stored for * the reshape level. @@ -290,11 +276,9 @@ void multires_reshape_smooth_object_grids_with_details( */ void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Displacement, space conversion. - * \{ */ +/* ================================================================================================ + * Displacement, space conversion. + */ /* Store original grid data, so then it's possible to calculate delta from it and add * high-frequency content on top of reshaped grids. */ @@ -303,11 +287,9 @@ void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_conte void multires_reshape_object_grids_to_tangent_displacement( const MultiresReshapeContext *reshape_context); -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Apply base. - * \{ */ +/* ================================================================================================ + * Apply base. + */ /* Update mesh coordinates to the final positions of displacement in object space. * This is effectively desired position of base mesh vertices after canceling out displacement. @@ -322,6 +304,4 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape /* Refine subdivision surface to the new positions of the base mesh. */ void multires_reshape_apply_base_refine_subdiv(MultiresReshapeContext *reshape_context); -/** \} */ - #endif /* __BKE_INTERN_MULTIRES_RESHAPE_H__ */ diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index a916a8b87c6..175b0ee9187 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -43,9 +43,9 @@ #include "DEG_depsgraph_query.h" -/* -------------------------------------------------------------------- */ -/** \name Construct/destruct reshape context. - * \{ */ +/* ================================================================================================ + * Construct/destruct reshape context. + */ /* Create subdivision surface descriptor which is configured for surface evaluation at a given * multires modifier. */ @@ -269,11 +269,9 @@ void multires_reshape_context_free(MultiresReshapeContext *reshape_context) MEM_freeN(reshape_context->grid_to_face_index); } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Helper accessors. - * \{ */ +/* ================================================================================================ + * Helper accessors. + */ /* For the given grid index get index of face it was created for. */ int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context, @@ -447,11 +445,9 @@ ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord( return grid_element; } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sample limit surface of the base mesh. - * \{ */ +/* ================================================================================================ + * Sample limit surface of the base mesh. + */ void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord, @@ -471,11 +467,9 @@ void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *resha reshape_context, face_index, corner, dPdu, dPdv, r_tangent_matrix); } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Custom data preparation. - * \{ */ +/* ================================================================================================ + * Custom data preparation. + */ static void allocate_displacement_grid(MDisps *displacement_grid, const int level) { @@ -537,11 +531,9 @@ void multires_reshape_ensure_grids(Mesh *mesh, const int level) ensure_mask_grids(mesh, level); } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Displacement, space conversion. - * \{ */ +/* ================================================================================================ + * Displacement, space conversion. + */ void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context) { @@ -678,13 +670,10 @@ void multires_reshape_object_grids_to_tangent_displacement( NULL); } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name MDISPS. - * \{ */ - -/* TODO(sergey): Make foreach_grid_coordinate more accessible and move this functionality to +/* ================================================================================================ + * MDISPS + * + * TODO(sergey): Make foreach_grid_coordinate more accessible and move this functionality to * own file. */ static void assign_final_coords_from_mdisps(const MultiresReshapeContext *reshape_context, @@ -735,5 +724,3 @@ void multires_reshape_assign_final_coords_from_orig_mdisps( foreach_grid_coordinate( reshape_context, reshape_context->top.level, assign_final_coords_from_orig_mdisps, NULL); } - -/** \} */ -- cgit v1.2.3 From 8cb463f4ff24f05dde858f9706a2990c6c81d817 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 17 Mar 2020 13:53:19 +0100 Subject: OverlayEngine: crash when using hidden faces Unreported Crash. When hidden faces are active (retopology) the depth test could fail as the default framebuffers aren't set. This patch will check if we are rendering a depth only and skip the clearing of the buffer. --- source/blender/draw/engines/overlay/overlay_edit_mesh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index 6c838568260..400947ea819 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -398,7 +398,7 @@ void OVERLAY_edit_mesh_draw(OVERLAY_Data *vedata) DRW_draw_pass(psl->edit_mesh_normals_ps); overlay_edit_mesh_draw_components(psl, pd, false); - if (v3d->shading.type == OB_SOLID && pd->edit_mesh.ghost_ob == 1 && + if (!DRW_state_is_depth() && v3d->shading.type == OB_SOLID && pd->edit_mesh.ghost_ob == 1 && pd->edit_mesh.edit_ob == 1) { /* In the case of single ghost object edit (common case for retopology): * we clear the depth buffer so that only the depth of the retopo mesh -- cgit v1.2.3 From 9ce38909500a47beff87081147178d7a52449df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Grzeli=C5=84ski?= Date: Tue, 17 Mar 2020 14:44:45 +0100 Subject: Cleanup: rename function This function was missed in rBec471a9b1c1. Differential Revision: https://developer.blender.org/D7155 --- source/blender/makesrna/intern/rna_space.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 1cd499520d9..66aeab725ea 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4376,7 +4376,7 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_api_region_view3d(srna); } -static void rna_def_space_buttons(BlenderRNA *brna) +static void rna_def_space_properties(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; @@ -6515,7 +6515,7 @@ void RNA_def_space(BlenderRNA *brna) rna_def_space_filebrowser(brna); rna_def_space_outliner(brna); rna_def_space_view3d(brna); - rna_def_space_buttons(brna); + rna_def_space_properties(brna); rna_def_space_dopesheet(brna); rna_def_space_graph(brna); rna_def_space_nla(brna); -- cgit v1.2.3 From b852db57ba24adfcfaa0ada7e9ff513a79a399a2 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 17 Mar 2020 12:29:36 +0100 Subject: Add experimental global undo speedup. The feature is hidden behind an experimental option, you'll have to enable it in the preferences to try it. This feature is not yet considered fully stable, crashes may happen, as well as .blend file corruptions (very unlikely, but still possible). In a nutshell, the ideas behind this code are to: * Detect unchanged IDs across an undo step. * Reuse as much as possible existing IDs memory, even when its content did change. * Re-use existing depsgraphs instead of building new ones from scratch. * Store accumulated recalc flags, to avoid needless re-compute of things that did not change, when the ID itself is detected as modified. See T60695 and D6580 for more technical details. --- release/scripts/startup/bl_ui/space_userpref.py | 21 ++ source/blender/blenkernel/BKE_blender_undo.h | 5 +- source/blender/blenkernel/BKE_main.h | 5 + source/blender/blenkernel/BKE_undo_system.h | 3 + source/blender/blenkernel/intern/blender_undo.c | 13 +- source/blender/blenkernel/intern/blendfile.c | 38 +-- source/blender/blenloader/BLO_readfile.h | 9 +- source/blender/blenloader/BLO_undofile.h | 4 + source/blender/blenloader/intern/readblenentry.c | 12 +- source/blender/blenloader/intern/readfile.c | 320 ++++++++++++++++++++--- source/blender/blenloader/intern/readfile.h | 23 +- source/blender/blenloader/intern/undofile.c | 9 +- source/blender/blenloader/intern/writefile.c | 6 + source/blender/editors/undo/memfile_undo.c | 120 ++++++++- source/blender/makesdna/DNA_ID.h | 4 + source/blender/makesdna/DNA_userdef_types.h | 3 +- source/blender/makesrna/intern/rna_userdef.c | 8 + 17 files changed, 534 insertions(+), 69 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 6aa0f51c55c..2ca0e73bd74 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2133,6 +2133,25 @@ class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): """ +class USERPREF_PT_experimental_system(ExperimentalPanel, Panel): + bl_label = "System" + + def draw(self, context): + prefs = context.preferences + experimental = prefs.experimental + + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + task = "T60695" + split = layout.split(factor=0.66) + col = split.split() + col.prop(experimental, "use_undo_speedup") + col = split.split() + col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task + + # ----------------------------------------------------------------------------- # Class Registration @@ -2222,6 +2241,8 @@ classes = ( # Popovers. USERPREF_PT_ndof_settings, + USERPREF_PT_experimental_system, + # Add dynamically generated editor theme panels last, # so they show up last in the theme section. *ThemeGenericClassGenerator.generate_panel_classes_from_theme_areas(), diff --git a/source/blender/blenkernel/BKE_blender_undo.h b/source/blender/blenkernel/BKE_blender_undo.h index 7392d3947a2..4ecedbbfc1e 100644 --- a/source/blender/blenkernel/BKE_blender_undo.h +++ b/source/blender/blenkernel/BKE_blender_undo.h @@ -32,7 +32,10 @@ struct bContext; struct MemFileUndoData *BKE_memfile_undo_encode(struct Main *bmain, struct MemFileUndoData *mfu_prev); -bool BKE_memfile_undo_decode(struct MemFileUndoData *mfu, struct bContext *C); +bool BKE_memfile_undo_decode(struct MemFileUndoData *mfu, + const int undo_direction, + const bool use_old_bmain_data, + struct bContext *C); void BKE_memfile_undo_free(struct MemFileUndoData *mfu); #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index a263162e013..8aac09d8738 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -99,6 +99,11 @@ typedef struct Main { * use "needs_flush_to_id" in edit data to flag data which needs updating. */ char is_memfile_undo_flush_needed; + /** + * Indicates that next memfile undo step should not allow to re-use old bmain when re-read, but + * instead do a complete full re-read/update from stored memfile. + */ + char use_memfile_full_barrier; BlendThumbnail *blen_thumb; diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h index c503215be1f..4870b19fe1d 100644 --- a/source/blender/blenkernel/BKE_undo_system.h +++ b/source/blender/blenkernel/BKE_undo_system.h @@ -83,6 +83,9 @@ typedef struct UndoStep { bool skip; /** Some situations require the global state to be stored, edge cases when exiting modes. */ bool use_memfile_step; + /** When this is true, undo/memfile read code is allowed to re-use old data-blocks for unchanged + * IDs, and existing depsgraphes. This has to be forbidden in some cases (like renamed IDs). */ + bool use_old_bmain_data; /** For use by undo systems that accumulate changes (text editor, painting). */ bool is_applied; /* Over alloc 'type->struct_size'. */ diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index 9ccc53b6318..bb705e2295c 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -61,7 +61,10 @@ #define UNDO_DISK 0 -bool BKE_memfile_undo_decode(MemFileUndoData *mfu, bContext *C) +bool BKE_memfile_undo_decode(MemFileUndoData *mfu, + const int undo_direction, + const bool use_old_bmain_data, + bContext *C) { Main *bmain = CTX_data_main(C); char mainstr[sizeof(bmain->name)]; @@ -76,8 +79,12 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu, bContext *C) success = BKE_blendfile_read(C, mfu->filename, &(const struct BlendFileReadParams){0}, NULL); } else { - success = BKE_blendfile_read_from_memfile( - C, &mfu->memfile, &(const struct BlendFileReadParams){0}, NULL); + struct BlendFileReadParams params = {0}; + params.undo_direction = undo_direction > 0 ? 1 : -1; + if (!use_old_bmain_data) { + params.skip_flags |= BLO_READ_SKIP_UNDO_OLD_MAIN; + } + success = BKE_blendfile_read_from_memfile(C, &mfu->memfile, ¶ms, NULL); } /* Restore, bmain has been re-allocated. */ diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index efab2039dc3..3890bf4e7f8 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -134,26 +134,29 @@ static void setup_app_userdef(BlendFileData *bfd) static void setup_app_data(bContext *C, BlendFileData *bfd, const char *filepath, - const bool is_startup, + const struct BlendFileReadParams *params, ReportList *reports) { Main *bmain = G_MAIN; Scene *curscene = NULL; const bool recover = (G.fileflags & G_FILE_RECOVER) != 0; + const bool is_startup = params->is_startup; enum { LOAD_UI = 1, LOAD_UI_OFF, LOAD_UNDO, } mode; - /* may happen with library files - UNDO file should never have NULL cursccene... */ - if (ELEM(NULL, bfd->curscreen, bfd->curscene)) { + if (params->undo_direction != 0) { + BLI_assert(bfd->curscene != NULL); + mode = LOAD_UNDO; + } + /* may happen with library files - UNDO file should never have NULL curscene (but may have a + * NULL curscreen)... */ + else if (ELEM(NULL, bfd->curscreen, bfd->curscene)) { BKE_report(reports, RPT_WARNING, "Library file, loading empty scene"); mode = LOAD_UI_OFF; } - else if (BLI_listbase_is_empty(&bfd->main->screens)) { - mode = LOAD_UNDO; - } else if (G.fileflags & G_FILE_NO_UI) { mode = LOAD_UI_OFF; } @@ -371,7 +374,9 @@ static void setup_app_data(bContext *C, * means that we do not reset their user count, however we do increase that one when doing * lib_link on local IDs using linked ones. * There is no real way to predict amount of changes here, so we have to fully redo - * refcounting . */ + * refcounting. + * Now that we re-use (and do not liblink in readfile.c) most local datablocks as well, we have + * to recompute refcount for all local IDs too. */ BKE_main_id_refcount_recompute(bmain, false); } } @@ -386,7 +391,7 @@ static void setup_app_blend_file_data(bContext *C, setup_app_userdef(bfd); } if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) { - setup_app_data(C, bfd, filepath, params->is_startup, reports); + setup_app_data(C, bfd, filepath, params, reports); } } @@ -473,16 +478,15 @@ bool BKE_blendfile_read_from_memfile(bContext *C, Main *bmain = CTX_data_main(C); BlendFileData *bfd; - bfd = BLO_read_from_memfile( - bmain, BKE_main_blendfile_path(bmain), memfile, params->skip_flags, reports); + bfd = BLO_read_from_memfile(bmain, BKE_main_blendfile_path(bmain), memfile, params, reports); if (bfd) { - /* remove the unused screens and wm */ - while (bfd->main->wm.first) { - BKE_id_free(bfd->main, bfd->main->wm.first); - } - while (bfd->main->screens.first) { - BKE_id_free(bfd->main, bfd->main->screens.first); - } + /* Removing the unused workspaces, screens and wm is useless here, setup_app_data will switch + * those lists with the ones from old bmain, which freeing is much more efficient than + * individual calls to `BKE_id_free()`. + * Further more, those are expected to be empty anyway with new memfile reading code. */ + BLI_assert(BLI_listbase_is_empty(&bfd->main->wm)); + BLI_assert(BLI_listbase_is_empty(&bfd->main->workspaces)); + BLI_assert(BLI_listbase_is_empty(&bfd->main->screens)); setup_app_blend_file_data(C, bfd, "", params, reports); BLO_blendfiledata_free(bfd); diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 00dbe334356..8495caa91b5 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -76,8 +76,11 @@ typedef struct WorkspaceConfigFileData { } WorkspaceConfigFileData; struct BlendFileReadParams { - uint skip_flags : 2; /* eBLOReadSkip */ + uint skip_flags : 3; /* eBLOReadSkip */ uint is_startup : 1; + + /** Whether we are reading the memfile for an undo (< 0) or a redo (> 0). */ + int undo_direction : 2; }; /* skip reading some data-block types (may want to skip screen data too). */ @@ -85,6 +88,8 @@ typedef enum eBLOReadSkip { BLO_READ_SKIP_NONE = 0, BLO_READ_SKIP_USERDEF = (1 << 0), BLO_READ_SKIP_DATA = (1 << 1), + /** Do not attempt to re-use IDs from old bmain for unchanged ones in case of undo. */ + BLO_READ_SKIP_UNDO_OLD_MAIN = (1 << 2), } eBLOReadSkip; #define BLO_READ_SKIP_ALL (BLO_READ_SKIP_USERDEF | BLO_READ_SKIP_DATA) @@ -98,7 +103,7 @@ BlendFileData *BLO_read_from_memory(const void *mem, BlendFileData *BLO_read_from_memfile(struct Main *oldmain, const char *filename, struct MemFile *memfile, - eBLOReadSkip skip_flags, + const struct BlendFileReadParams *params, struct ReportList *reports); void BLO_blendfiledata_free(BlendFileData *bfd); diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h index 0388b3f3520..5f1142cc20e 100644 --- a/source/blender/blenloader/BLO_undofile.h +++ b/source/blender/blenloader/BLO_undofile.h @@ -34,6 +34,10 @@ typedef struct { unsigned int size; /** When true, this chunk doesn't own the memory, it's shared with a previous #MemFileChunk */ bool is_identical; + /** When true, this chunk is also identical to the one in the next step (used by undo code to + * detect unchanged IDs). + * Defined when writing the next step (i.e. last undo step has those always false). */ + bool is_identical_future; } MemFileChunk; typedef struct MemFile { diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index a4b96c9e59c..085e500f7e5 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -363,17 +363,17 @@ BlendFileData *BLO_read_from_memory(const void *mem, BlendFileData *BLO_read_from_memfile(Main *oldmain, const char *filename, MemFile *memfile, - eBLOReadSkip skip_flags, + const struct BlendFileReadParams *params, ReportList *reports) { BlendFileData *bfd = NULL; FileData *fd; ListBase old_mainlist; - fd = blo_filedata_from_memfile(memfile, reports); + fd = blo_filedata_from_memfile(memfile, params, reports); if (fd) { fd->reports = reports; - fd->skip_flags = skip_flags; + fd->skip_flags = params->skip_flags; BLI_strncpy(fd->relabase, filename, sizeof(fd->relabase)); /* clear ob->proxy_from pointers in old main */ @@ -384,6 +384,12 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, /* add the library pointers in oldmap lookup */ blo_add_library_pointer_map(&old_mainlist, fd); + if ((params->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0) { + /* Build idmap of old main (we only care about local data here, so we can do that after + * split_main() call. */ + blo_make_old_idmap_from_main(fd, old_mainlist.first); + } + /* makes lookup of existing images in old main */ blo_make_image_pointer_map(fd, oldmain); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index b8c79b3f064..c3fba697bd3 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -97,6 +97,7 @@ #include "BLI_endian_switch.h" #include "BLI_blenlib.h" +#include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_threads.h" #include "BLI_mempool.h" @@ -261,6 +262,7 @@ typedef struct BHeadN { /** When set, the remainder of this allocation is the data, otherwise it needs to be read. */ bool has_data; #endif + bool is_memchunk_identical; struct BHead bhead; } BHeadN; @@ -794,7 +796,7 @@ static BHeadN *get_bhead(FileData *fd) */ if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) { bhead4.code = DATA; - readsize = fd->read(fd, &bhead4, sizeof(bhead4)); + readsize = fd->read(fd, &bhead4, sizeof(bhead4), NULL); if (readsize == sizeof(bhead4) || bhead4.code == ENDB) { if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { @@ -817,7 +819,7 @@ static BHeadN *get_bhead(FileData *fd) } else { bhead8.code = DATA; - readsize = fd->read(fd, &bhead8, sizeof(bhead8)); + readsize = fd->read(fd, &bhead8, sizeof(bhead8), NULL); if (readsize == sizeof(bhead8) || bhead8.code == ENDB) { if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { @@ -858,6 +860,7 @@ static BHeadN *get_bhead(FileData *fd) new_bhead->next = new_bhead->prev = NULL; new_bhead->file_offset = fd->file_offset; new_bhead->has_data = false; + new_bhead->is_memchunk_identical = false; new_bhead->bhead = bhead; off64_t seek_new = fd->seek(fd, bhead.len, SEEK_CUR); if (seek_new == -1) { @@ -880,9 +883,10 @@ static BHeadN *get_bhead(FileData *fd) new_bhead->file_offset = 0; /* don't seek. */ new_bhead->has_data = true; #endif + new_bhead->is_memchunk_identical = false; new_bhead->bhead = bhead; - readsize = fd->read(fd, new_bhead + 1, bhead.len); + readsize = fd->read(fd, new_bhead + 1, bhead.len, &new_bhead->is_memchunk_identical); if (readsize != bhead.len) { fd->is_eof = true; @@ -972,7 +976,8 @@ static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf) success = false; } else { - if (fd->read(fd, buf, new_bhead->bhead.len) != new_bhead->bhead.len) { + if (fd->read(fd, buf, new_bhead->bhead.len, &new_bhead->is_memchunk_identical) != + new_bhead->bhead.len) { success = false; } } @@ -989,6 +994,7 @@ static BHead *blo_bhead_read_full(FileData *fd, BHead *thisblock) new_bhead_data->bhead = new_bhead->bhead; new_bhead_data->file_offset = new_bhead->file_offset; new_bhead_data->has_data = true; + new_bhead_data->is_memchunk_identical = false; if (!blo_bhead_read_data(fd, thisblock, new_bhead_data + 1)) { MEM_freeN(new_bhead_data); return NULL; @@ -1009,7 +1015,7 @@ static void decode_blender_header(FileData *fd) int readsize; /* read in the header data */ - readsize = fd->read(fd, header, sizeof(header)); + readsize = fd->read(fd, header, sizeof(header), NULL); if (readsize == sizeof(header) && STREQLEN(header, "BLENDER", 7) && ELEM(header[7], '_', '-') && ELEM(header[8], 'v', 'V') && @@ -1141,7 +1147,10 @@ static int *read_file_thumbnail(FileData *fd) /* Regular file reading. */ -static int fd_read_data_from_file(FileData *filedata, void *buffer, uint size) +static int fd_read_data_from_file(FileData *filedata, + void *buffer, + uint size, + bool *UNUSED(r_is_memchunck_identical)) { int readsize = read(filedata->filedes, buffer, size); @@ -1163,7 +1172,10 @@ static off64_t fd_seek_data_from_file(FileData *filedata, off64_t offset, int wh /* GZip file reading. */ -static int fd_read_gzip_from_file(FileData *filedata, void *buffer, uint size) +static int fd_read_gzip_from_file(FileData *filedata, + void *buffer, + uint size, + bool *UNUSED(r_is_memchunck_identical)) { int readsize = gzread(filedata->gzfiledes, buffer, size); @@ -1179,7 +1191,10 @@ static int fd_read_gzip_from_file(FileData *filedata, void *buffer, uint size) /* Memory reading. */ -static int fd_read_from_memory(FileData *filedata, void *buffer, uint size) +static int fd_read_from_memory(FileData *filedata, + void *buffer, + uint size, + bool *UNUSED(r_is_memchunck_identical)) { /* don't read more bytes then there are available in the buffer */ int readsize = (int)MIN2(size, (uint)(filedata->buffersize - filedata->file_offset)); @@ -1192,7 +1207,10 @@ static int fd_read_from_memory(FileData *filedata, void *buffer, uint size) /* MemFile reading. */ -static int fd_read_from_memfile(FileData *filedata, void *buffer, uint size) +static int fd_read_from_memfile(FileData *filedata, + void *buffer, + uint size, + bool *r_is_memchunck_identical) { static size_t seek = SIZE_MAX; /* the current position */ static size_t offset = 0; /* size of previous chunks */ @@ -1248,6 +1266,15 @@ static int fd_read_from_memfile(FileData *filedata, void *buffer, uint size) totread += readsize; filedata->file_offset += readsize; seek += readsize; + if (r_is_memchunck_identical != NULL) { + /* `is_identical` of current chunk represent whether it changed compared to previous undo + * step. this is fine in redo case (filedata->undo_direction > 0), but not in undo case, + * where we need an extra flag defined when saving the next (future) step after the one we + * want to restore, as we are supposed to 'come from' that future undo step, and not the + * one before current one. */ + *r_is_memchunck_identical = filedata->undo_direction > 0 ? chunk->is_identical : + chunk->is_identical_future; + } } while (totread < size); return totread; @@ -1414,7 +1441,10 @@ static FileData *blo_filedata_from_file_minimal(const char *filepath) return NULL; } -static int fd_read_gzip_from_memory(FileData *filedata, void *buffer, uint size) +static int fd_read_gzip_from_memory(FileData *filedata, + void *buffer, + uint size, + bool *UNUSED(r_is_memchunck_identical)) { int err; @@ -1485,7 +1515,9 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, ReportList *rep } } -FileData *blo_filedata_from_memfile(MemFile *memfile, ReportList *reports) +FileData *blo_filedata_from_memfile(MemFile *memfile, + const struct BlendFileReadParams *params, + ReportList *reports) { if (!memfile) { BKE_report(reports, RPT_WARNING, "Unable to open blend "); @@ -1494,6 +1526,7 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, ReportList *reports) else { FileData *fd = filedata_new(); fd->memfile = memfile; + fd->undo_direction = params->undo_direction; fd->read = fd_read_from_memfile; fd->flags |= FD_FLAGS_NOT_MY_BUFFER; @@ -1568,6 +1601,9 @@ void blo_filedata_free(FileData *fd) if (fd->libmap && !(fd->flags & FD_FLAGS_NOT_MY_LIBMAP)) { oldnewmap_free(fd->libmap); } + if (fd->old_idmap != NULL) { + BKE_main_idmap_destroy(fd->old_idmap); + } if (fd->bheadmap) { MEM_freeN(fd->bheadmap); } @@ -2187,6 +2223,16 @@ void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd) fd->old_mainlist = old_mainlist; } +/* Build a GSet of old main (we only care about local data here, so we can do that after + * split_main() call. */ +void blo_make_old_idmap_from_main(FileData *fd, Main *bmain) +{ + if (fd->old_idmap != NULL) { + BKE_main_idmap_destroy(fd->old_idmap); + } + fd->old_idmap = BKE_main_idmap_create(bmain, false, NULL, MAIN_IDMAP_TYPE_UUID); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -2267,6 +2313,10 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname) #endif } } + + if (!BHEADN_FROM_BHEAD(bh)->is_memchunk_identical) { + fd->are_memchunks_identical = false; + } #ifdef USE_BHEAD_READ_ON_DEMAND if (bh_orig != bh) { MEM_freeN(BHEADN_FROM_BHEAD(bh)); @@ -2656,17 +2706,17 @@ static void direct_link_id_override_property_cb(FileData *fd, void *data) link_list_ex(fd, &op->operations, direct_link_id_override_property_operation_cb); } -static void direct_link_id(FileData *fd, ID *id); +static void direct_link_id(FileData *fd, ID *id, ID *id_old); static void direct_link_nodetree(FileData *fd, bNodeTree *ntree); static void direct_link_collection(FileData *fd, Collection *collection); -static void direct_link_id_private_id(FileData *fd, ID *id) +static void direct_link_id_private_id(FileData *fd, ID *id, ID *id_old) { /* Handle 'private IDs'. */ bNodeTree **nodetree = BKE_ntree_ptr_from_id(id); if (nodetree != NULL && *nodetree != NULL) { *nodetree = newdataadr(fd, *nodetree); - direct_link_id(fd, (ID *)*nodetree); + direct_link_id(fd, (ID *)*nodetree, id_old != NULL ? (ID *)ntreeFromID(id_old) : NULL); direct_link_nodetree(fd, *nodetree); } @@ -2674,13 +2724,15 @@ static void direct_link_id_private_id(FileData *fd, ID *id) Scene *scene = (Scene *)id; if (scene->master_collection != NULL) { scene->master_collection = newdataadr(fd, scene->master_collection); - direct_link_id(fd, &scene->master_collection->id); + direct_link_id(fd, + &scene->master_collection->id, + id_old != NULL ? &((Scene *)id_old)->master_collection->id : NULL); direct_link_collection(fd, scene->master_collection); } } } -static void direct_link_id(FileData *fd, ID *id) +static void direct_link_id(FileData *fd, ID *id, ID *id_old) { /*link direct data of ID properties*/ if (id->properties) { @@ -2704,8 +2756,34 @@ static void direct_link_id(FileData *fd, ID *id) * * But for regular file load we clear the flag, since the flags might have been changed since * the version the file has been saved with. */ - if (!fd->memfile) { + if (fd->memfile == NULL) { id->recalc = 0; + id->recalc_undo_accumulated = 0; + } + else if ((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0) { + if (fd->undo_direction < 0) { + /* We are coming from the future (i.e. do an actual undo, and not a redo), and we found an + * old (aka existing) ID: we use its 'accumulated recalc flags since last memfile undo step + * saving' as recalc flags of our newly read ID. */ + if (id_old != NULL) { + id->recalc = id_old->recalc_undo_accumulated; + } + } + else { + /* We are coming from the past (i.e. do a redo), we use saved 'accumulated + * recalc flags since last memfile undo step saving' as recalc flags of our newly read ID. */ + id->recalc = id->recalc_undo_accumulated; + } + /* In any case, we need to flush the depsgraph's CoWs, as even if the ID address itself did not + * change, internal data most likely have. */ + id->recalc |= ID_RECALC_COPY_ON_WRITE; + + /* We need to 'accumulate' the accumulated recalc flags of all undo steps until we actually + * perform a depsgraph update, otherwise we'd only ever use the flags from one of the steps, + * and never get proper flags matching all others. */ + if (id_old != NULL) { + id->recalc_undo_accumulated |= id_old->recalc_undo_accumulated; + } } /* Link direct data of overrides. */ @@ -2721,7 +2799,7 @@ static void direct_link_id(FileData *fd, ID *id) } /* Handle 'private IDs'. */ - direct_link_id_private_id(fd, id); + direct_link_id_private_id(fd, id, id_old); } /** \} */ @@ -9071,15 +9149,155 @@ static BHead *read_libblock(FileData *fd, } /* read libblock */ + fd->are_memchunks_identical = true; id = read_struct(fd, bhead, "lib block"); + const short idcode = id != NULL ? GS(id->name) : 0; + + BHead *id_bhead = bhead; + /* Used when undoing from memfile, we swap changed IDs into their old addresses when found. */ + ID *id_old = NULL; + bool do_id_swap = false; + + if (id != NULL) { + const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0; + + if (id_bhead->code != ID_LINK_PLACEHOLDER) { + /* need a name for the mallocN, just for debugging and sane prints on leaks */ + allocname = dataname(idcode); + + /* read all data into fd->datamap */ + /* TODO: instead of building oldnewmap here we could just quickly check the bheads... could + * save some more ticks. Probably not worth it though, bottleneck is full depsgraph rebuild + * and eval, not actual file reading. */ + bhead = read_data_into_oldnewmap(fd, id_bhead, allocname); + + DEBUG_PRINTF( + "%s: ID %s is unchanged: %d\n", __func__, id->name, fd->are_memchunks_identical); + + if (fd->memfile != NULL) { + BLI_assert(fd->old_idmap != NULL || !do_partial_undo); + /* This code should only ever be reached for local data-blocks. */ + BLI_assert(main->curlib == NULL); + + /* Find the 'current' existing ID we want to reuse instead of the one we would read from + * the undo memfile. */ + DEBUG_PRINTF("\t Looking for ID %s with uuid %u instead of newly read one\n", + id->name, + id->session_uuid); + id_old = do_partial_undo ? BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid) : + NULL; + bool can_finalize_and_return = false; + + if (ELEM(idcode, ID_WM, ID_SCR, ID_WS)) { + /* Read WindowManager, Screen and WorkSpace IDs are never actually used during undo (see + * `setup_app_data()` in `blendfile.c`). + * So we can just abort here, just ensuring libmapping is set accordingly. */ + can_finalize_and_return = true; + } + else if (id_old != NULL && fd->are_memchunks_identical) { + /* Do not add LIB_TAG_NEW here, this should not be needed/used in undo case anyway (as + * this is only for do_version-like code), but for sake of consistency, and also because + * it will tell us which ID is re-used from old Main, and which one is actually new. */ + id_old->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_UNDO_OLD_ID_REUSED; + id_old->lib = main->curlib; + id_old->us = ID_FAKE_USERS(id_old); + /* Do not reset id->icon_id here, memory allocated for it remains valid. */ + /* Needed because .blend may have been saved with crap value here... */ + id_old->newid = NULL; + id_old->orig_id = NULL; + + /* About recalc: since that ID did not change at all, we know that its recalc fields also + * remained unchanged, so no need to handle neither recalc nor recalc_undo_future here. + */ + + Main *old_bmain = fd->old_mainlist->first; + ListBase *old_lb = which_libbase(old_bmain, idcode); + ListBase *new_lb = which_libbase(main, idcode); + BLI_remlink(old_lb, id_old); + BLI_addtail(new_lb, id_old); + + can_finalize_and_return = true; + } + + if (can_finalize_and_return) { + DEBUG_PRINTF("Re-using existing ID %s instead of newly read one\n", id_old->name); + oldnewmap_insert(fd->libmap, id_bhead->old, id_old, id_bhead->code); + oldnewmap_insert(fd->libmap, id_old, id_old, id_bhead->code); + + if (r_id) { + *r_id = id_old; + } + + if (do_partial_undo) { + /* Even though we re-use the old ID as-is, it does not mean that we are 100% safe from + * needing some depsgraph updates for it (it could depend on another ID which address + * did + * not change, but which actual content might have been re-read from the memfile). */ + if (fd->undo_direction < 0) { + /* We are coming from the future (i.e. do an actual undo, and not a redo), we use our + * old reused ID's 'accumulated recalc flags since last memfile undo step saving' as + * recalc flags. */ + id_old->recalc = id_old->recalc_undo_accumulated; + } + else { + /* We are coming from the past (i.e. do a redo), we use the saved 'accumulated recalc + * flags since last memfile undo step saving' from the newly read ID as recalc flags. + */ + id_old->recalc = id->recalc_undo_accumulated; + } + /* There is no need to flush the depsgraph's CoWs here, since that ID's data itself did + * not change. */ + + /* We need to 'accumulate' the accumulated recalc flags of all undo steps until we + * actually perform a depsgraph update, otherwise we'd only ever use the flags from one + * of the steps, and never get proper flags matching all others. */ + id_old->recalc_undo_accumulated |= id->recalc_undo_accumulated; + } + + MEM_freeN(id); + oldnewmap_free_unused(fd->datamap); + oldnewmap_clear(fd->datamap); + + return bhead; + } + } + } - if (id) { - const short idcode = GS(id->name); /* do after read_struct, for dna reconstruct */ lb = which_libbase(main, idcode); if (lb) { + /* Some re-used old IDs might also use newly read ones, so we have to check for old memory + * addresses for those as well. */ + if (fd->memfile != NULL && do_partial_undo && id->lib == NULL) { + BLI_assert(fd->old_idmap != NULL); + DEBUG_PRINTF("\t Looking for ID %s with uuid %u instead of newly read one\n", + id->name, + id->session_uuid); + id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid); + if (id_old != NULL) { + BLI_assert(MEM_allocN_len(id) == MEM_allocN_len(id_old)); + /* UI IDs are always re-used from old bmain at higher-level calling code, so never swap + * those. Besides maybe custom properties, no other ID should have pointers to those + * anyway... + * And linked IDs are handled separately as well. */ + do_id_swap = !ELEM(idcode, ID_WM, ID_SCR, ID_WS) && + !(id_bhead->code == ID_LINK_PLACEHOLDER); + } + } + + /* At this point, we know we are going to keep that newly read & allocated ID, so we need to + * reallocate it to ensure we actually get a unique memory address for it. */ + if (!do_id_swap) { + DEBUG_PRINTF("using newly-read ID %s to a new mem address\n", id->name); + } + else { + DEBUG_PRINTF("using newly-read ID %s to its old, already existing address\n", id->name); + } + /* for ID_LINK_PLACEHOLDER check */ - oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); + ID *id_target = do_id_swap ? id_old : id; + oldnewmap_insert(fd->libmap, id_bhead->old, id_target, id_bhead->code); + oldnewmap_insert(fd->libmap, id_old, id_target, id_bhead->code); BLI_addtail(lb, id); @@ -9100,10 +9318,10 @@ static BHead *read_libblock(FileData *fd, } if (r_id) { - *r_id = id; + *r_id = do_id_swap ? id_old : id; } if (!id) { - return blo_bhead_next(fd, bhead); + return blo_bhead_next(fd, id_bhead); } id->lib = main->curlib; @@ -9113,7 +9331,7 @@ static BHead *read_libblock(FileData *fd, id->orig_id = NULL; /* this case cannot be direct_linked: it's just the ID part */ - if (bhead->code == ID_LINK_PLACEHOLDER) { + if (id_bhead->code == ID_LINK_PLACEHOLDER) { /* That way, we know which data-lock needs do_versions (required currently for linking). */ id->tag = tag | LIB_TAG_ID_LINK_PLACEHOLDER | LIB_TAG_NEED_LINK | LIB_TAG_NEW; @@ -9126,23 +9344,17 @@ static BHead *read_libblock(FileData *fd, } } - return blo_bhead_next(fd, bhead); + return blo_bhead_next(fd, id_bhead); } - /* need a name for the mallocN, just for debugging and sane prints on leaks */ - allocname = dataname(GS(id->name)); - - /* read all data into fd->datamap */ - bhead = read_data_into_oldnewmap(fd, bhead, allocname); - /* init pointers direct data */ - direct_link_id(fd, id); + direct_link_id(fd, id, id_old); /* That way, we know which data-lock needs do_versions (required currently for linking). */ - /* Note: doing this after driect_link_id(), which resets that field. */ + /* Note: doing this after direct_link_id(), which resets that field. */ id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW; - switch (GS(id->name)) { + switch (idcode) { case ID_WM: direct_link_windowmanager(fd, (wmWindowManager *)id); break; @@ -9266,6 +9478,37 @@ static BHead *read_libblock(FileData *fd, *r_id = NULL; } } + else if (do_id_swap) { + /* During memfile undo, if an ID changed and we cannot directly re-use existing one from old + * bmain, we do a full read of the new id from the memfile, and then fully swap its content + * with the old id. This allows us to keep the same pointer even for modified data, which helps + * reducing further detected changes by the depsgraph (since unchanged IDs remain fully + * unchanged, even if they are using/pointing to a changed one). */ + + BLI_assert((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0); + + Main *old_bmain = fd->old_mainlist->first; + BLI_assert(id_old != NULL); + + ListBase *old_lb = which_libbase(old_bmain, idcode); + ListBase *new_lb = which_libbase(main, idcode); + BLI_remlink(old_lb, id_old); + BLI_remlink(new_lb, id); + + /* We do not need any remapping from this call here, since no ID pointer is valid in the data + * currently (they are all pointing to old addresses, and need to go through `lib_link` + * process). So we can pass NULL for the Main pointer parameter. */ + BKE_lib_id_swap_full(NULL, id, id_old); + + BLI_addtail(new_lb, id_old); + BLI_addtail(old_lb, id); + } + else if (fd->memfile != NULL) { + DEBUG_PRINTF("We had to fully re-recreate ID %s (old addr: %p, new addr: %p)...\n", + id->name, + id_old, + id); + } return (bhead); } @@ -9437,6 +9680,8 @@ static void do_versions_after_linking(Main *main, ReportList *reports) static void lib_link_all(FileData *fd, Main *bmain) { + const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0; + ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { if ((id->tag & LIB_TAG_NEED_LINK) == 0) { @@ -9450,6 +9695,13 @@ static void lib_link_all(FileData *fd, Main *bmain) continue; } + if (fd->memfile != NULL && do_partial_undo && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) { + /* This ID has been re-used from 'old' bmain. Since it was therfore unchanged accross current + * undo step, and old IDs re-use their old memory address, we do not need to liblink it at + * all. */ + continue; + } + lib_link_id(fd, bmain, id); /* Note: ID types are processed in reverse order as defined by INDEX_ID_XXX enums in DNA_ID.h. diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index fb307f6bde3..4965845d167 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -30,6 +30,8 @@ #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" /* for ReportType */ +struct IDNameLib_Map; +struct GSet; struct Key; struct MemFile; struct Object; @@ -38,6 +40,8 @@ struct PartEff; struct ReportList; struct View3D; +typedef struct IDNameLib_Map IDNameLib_Map; + enum eFileDataFlag { FD_FLAGS_SWITCH_ENDIAN = 1 << 0, FD_FLAGS_FILE_POINTSIZE_IS_4 = 1 << 1, @@ -57,7 +61,10 @@ enum eFileDataFlag { typedef int64_t off64_t; #endif -typedef int(FileDataReadFn)(struct FileData *filedata, void *buffer, unsigned int size); +typedef int(FileDataReadFn)(struct FileData *filedata, + void *buffer, + unsigned int size, + bool *r_is_memchunk_identical); typedef off64_t(FileDataSeekFn)(struct FileData *filedata, off64_t offset, int whence); typedef struct FileData { @@ -78,6 +85,14 @@ typedef struct FileData { const char *buffer; /** Variables needed for reading from memfile (undo). */ struct MemFile *memfile; + /** Whether all data read from memfile so far was identical + * (i.e. shared with some previous undo step). + * Updated by `fd_read_from_memfile()`, user is responsible to reset it to true when needed. + * Used to detect unchanged IDs. */ + bool are_memchunks_identical; + /** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use + * to detect unchanged data from memfile. */ + short undo_direction; /** Variables needed for reading from file. */ gzFile gzfiledes; @@ -120,6 +135,7 @@ typedef struct FileData { ListBase *mainlist; /** Used for undo. */ ListBase *old_mainlist; + struct IDNameLib_Map *old_idmap; struct ReportList *reports; } FileData; @@ -135,7 +151,9 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath); FileData *blo_filedata_from_file(const char *filepath, struct ReportList *reports); FileData *blo_filedata_from_memory(const void *buffer, int buffersize, struct ReportList *reports); -FileData *blo_filedata_from_memfile(struct MemFile *memfile, struct ReportList *reports); +FileData *blo_filedata_from_memfile(struct MemFile *memfile, + const struct BlendFileReadParams *params, + struct ReportList *reports); void blo_clear_proxy_pointers_from_lib(struct Main *oldmain); void blo_make_image_pointer_map(FileData *fd, struct Main *oldmain); @@ -149,6 +167,7 @@ void blo_end_sound_pointer_map(FileData *fd, struct Main *oldmain); void blo_make_packed_pointer_map(FileData *fd, struct Main *oldmain); void blo_end_packed_pointer_map(FileData *fd, struct Main *oldmain); void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd); +void blo_make_old_idmap_from_main(FileData *fd, struct Main *bmain); void blo_filedata_free(FileData *fd); diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c index 95a4771b313..0bbd8c26fa1 100644 --- a/source/blender/blenloader/intern/undofile.c +++ b/source/blender/blenloader/intern/undofile.c @@ -98,6 +98,7 @@ void memfile_chunk_add(MemFile *memfile, const char *buf, uint size, MemFileChun curchunk->size = size; curchunk->buf = NULL; curchunk->is_identical = false; + curchunk->is_identical_future = false; BLI_addtail(&memfile->chunks, curchunk); /* we compare compchunk with buf */ @@ -107,6 +108,7 @@ void memfile_chunk_add(MemFile *memfile, const char *buf, uint size, MemFileChun if (memcmp(compchunk->buf, buf, size) == 0) { curchunk->buf = compchunk->buf; curchunk->is_identical = true; + compchunk->is_identical_future = true; } } *compchunk_step = compchunk->next; @@ -126,8 +128,11 @@ struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Scene **r_scene) { struct Main *bmain_undo = NULL; - BlendFileData *bfd = BLO_read_from_memfile( - oldmain, BKE_main_blendfile_path(oldmain), memfile, BLO_READ_SKIP_NONE, NULL); + BlendFileData *bfd = BLO_read_from_memfile(oldmain, + BKE_main_blendfile_path(oldmain), + memfile, + &(const struct BlendFileReadParams){0}, + NULL); if (bfd) { bmain_undo = bfd->main; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 1e50cda3eaf..29366e3bae5 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -4027,6 +4027,12 @@ static bool write_file_handle(Main *mainvar, if (do_override) { BKE_lib_override_library_operations_store_end(override_storage, id); } + + if (wd->use_memfile) { + /* Very important to do it after every ID write now, otherwise we cannot know whether a + * specific ID changed or not. */ + mywrite_flush(wd); + } } mywrite_flush(wd); diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c index a5f30409aa6..312edab91b1 100644 --- a/source/blender/editors/undo/memfile_undo.c +++ b/source/blender/editors/undo/memfile_undo.c @@ -23,12 +23,20 @@ #include "BLI_utildefines.h" #include "BLI_sys_types.h" +#include "BLI_ghash.h" + #include "DNA_object_enums.h" +#include "DNA_object_types.h" #include "BKE_blender_undo.h" #include "BKE_context.h" -#include "BKE_undo_system.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" #include "BKE_main.h" +#include "BKE_scene.h" +#include "BKE_undo_system.h" + +#include "../depsgraph/DEG_depsgraph.h" #include "WM_api.h" #include "WM_types.h" @@ -85,16 +93,102 @@ static bool memfile_undosys_step_encode(struct bContext *UNUSED(C), us->data = BKE_memfile_undo_encode(bmain, us_prev ? us_prev->data : NULL); us->step.data_size = us->data->undo_size; + /* Store the fact that we should not re-use old data with that undo step, and reset the Main + * flag. */ + us->step.use_old_bmain_data = !bmain->use_memfile_full_barrier; + bmain->use_memfile_full_barrier = false; + return true; } -static void memfile_undosys_step_decode( - struct bContext *C, struct Main *bmain, UndoStep *us_p, int UNUSED(dir), bool UNUSED(is_final)) +static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data) +{ + ID *id_self = cb_data->id_self; + ID **id_pointer = cb_data->id_pointer; + BLI_assert((id_self->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0); + Main *bmain = cb_data->user_data; + + ID *id = *id_pointer; + if (id != NULL && id->lib == NULL && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) == 0) { + bool do_stop_iter = true; + if (GS(id_self->name) == ID_OB) { + Object *ob_self = (Object *)id_self; + if (ob_self->type == OB_ARMATURE) { + if (ob_self->data == id) { + BLI_assert(GS(id->name) == ID_AR); + if (ob_self->pose != NULL) { + /* We have a changed/re-read armature used by an unchanged armature object: our beloved + * Bone pointers from the object's pose need their usual special treatment. */ + ob_self->pose->flag |= POSE_RECALC; + } + } + else { + /* Cannot stop iteration until we checked ob_self->data pointer... */ + do_stop_iter = false; + } + } + } + + /* In case an old, re-used ID is using a newly read data-block (i.e. one of its ID pointers got + * updated), we have to tell the depsgraph about it. */ + DEG_id_tag_update_ex(bmain, id_self, ID_RECALC_COPY_ON_WRITE); + return do_stop_iter ? IDWALK_RET_STOP_ITER : IDWALK_RET_NOP; + } + + return IDWALK_RET_NOP; +} + +static void memfile_undosys_step_decode(struct bContext *C, + struct Main *bmain, + UndoStep *us_p, + int undo_direction, + bool UNUSED(is_final)) { + BLI_assert(undo_direction != 0); + + bool use_old_bmain_data = true; + + if (!U.experimental.use_undo_speedup) { + use_old_bmain_data = false; + } + else if (undo_direction > 0) { + /* Redo case. + * The only time we should have to force a complete redo is when current step is tagged as a + * redo barrier. + * If previous step was not a memfile one should not matter here, current data in old bmain + * should still always be valid for unchanged dtat-blocks. */ + if (us_p->use_old_bmain_data == false) { + use_old_bmain_data = false; + } + } + else { + /* Undo case. + * Here we do not care whether current step is an undo barrier, since we are comming from 'the + * future' we can still re-use old data. However, if *next* undo step (i.e. the one immédiately + * in the future, the one we are comming from) is a barrier, then we have to force a complete + * undo. + * Note that non-memfile undo steps **should** not be an issue anymore, since we handle + * fine-grained update flags now. + */ + UndoStep *us_next = us_p->next; + if (us_next != NULL) { + if (us_next->use_old_bmain_data == false) { + use_old_bmain_data = false; + } + } + } + + /* Extract depsgraphs from current bmain (which may be freed during undo step reading), + * and store them for re-use. */ + GHash *depsgraphs = NULL; + if (use_old_bmain_data) { + depsgraphs = BKE_scene_undo_depsgraphs_extract(bmain); + } + ED_editors_exit(bmain, false); MemFileUndoStep *us = (MemFileUndoStep *)us_p; - BKE_memfile_undo_decode(us->data, C); + BKE_memfile_undo_decode(us->data, undo_direction, use_old_bmain_data, C); for (UndoStep *us_iter = us_p->next; us_iter; us_iter = us_iter->next) { if (BKE_UNDOSYS_TYPE_IS_MEMFILE_SKIP(us_iter->type)) { @@ -113,6 +207,24 @@ static void memfile_undosys_step_decode( bmain = CTX_data_main(C); ED_editors_init_for_undo(bmain); + if (use_old_bmain_data) { + /* Restore previous depsgraphs into current bmain. */ + BKE_scene_undo_depsgraphs_restore(bmain, depsgraphs); + + /* We need to inform depsgraph about re-used old IDs that would be using newly read + * data-blocks, at least COW evaluated copies need to be updated... */ + ID *id = NULL; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) { + BKE_library_foreach_ID_link( + bmain, id, memfile_undosys_step_id_reused_cb, bmain, IDWALK_READONLY); + } + } + FOREACH_MAIN_ID_END; + + BKE_main_id_tag_all(bmain, LIB_TAG_UNDO_OLD_ID_REUSED, false); + } + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C)); } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index f2923c7f144..063ea04bdba 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -559,6 +559,10 @@ enum { /* Datablock was not allocated by standard system (BKE_libblock_alloc), do not free its memory * (usual type-specific freeing is called though). */ LIB_TAG_NOT_ALLOCATED = 1 << 18, + + /* RESET_AFTER_USE Used by undo system to tag unchanged IDs re-used from old Main (instead of + * read from memfile). */ + LIB_TAG_UNDO_OLD_ID_REUSED = 1 << 19, }; /* Tag given ID for an update in all the dependency graphs. */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 6961a9e9c3e..904d7b8a52e 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -613,7 +613,8 @@ typedef struct UserDef_FileSpaceData { } UserDef_FileSpaceData; typedef struct UserDef_Experimental { - char _pad0[8]; /* makesdna does not allow empty structs. */ + char use_undo_speedup; + char _pad0[7]; /* makesdna does not allow empty structs. */ } UserDef_Experimental; #define USER_EXPERIMENTAL_TEST(userdef, member) \ diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index a153c1dda1e..195a80a1101 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -5918,12 +5918,20 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) static void rna_def_userdef_experimental(BlenderRNA *brna) { StructRNA *srna; + PropertyRNA *prop; srna = RNA_def_struct(brna, "PreferencesExperimental", NULL); RNA_def_struct_sdna(srna, "UserDef_Experimental"); RNA_def_struct_nested(brna, srna, "Preferences"); RNA_def_struct_clear_flag(srna, STRUCT_UNDO); RNA_def_struct_ui_text(srna, "Experimental", "Experimental features"); + + prop = RNA_def_property(srna, "use_undo_speedup", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_undo_speedup", 1); + RNA_def_property_ui_text( + prop, + "Undo Speedup", + "Use new undo speedup (WARNING: can lead to crashes and serious .blend file corruption)"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) -- cgit v1.2.3 From 20456b52b47965b2d4ee5d9fc831ff8d821e0007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Barschkis?= Date: Tue, 17 Mar 2020 16:11:12 +0100 Subject: Fluid: Fixes for new abort bake faster feature In addition to previous commit that made it possible to abort bakes faster. --- source/blender/blenkernel/intern/fluid.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 2a94b56b68c..797e03e015a 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -3535,6 +3535,8 @@ static int manta_step( /* Store baking success - bake might be aborted anytime by user. */ int result = 1; + int mode = mds->cache_type; + bool mode_replay = (mode == FLUID_DOMAIN_CACHE_REPLAY); /* Update object state. */ invert_m4_m4(mds->imat, ob->obmat); @@ -3571,7 +3573,7 @@ static int manta_step( update_flowsfluids(depsgraph, scene, ob, mds, time_per_frame, frame_length, frame, dt); /* If user requested stop, quit baking */ - if (G.is_break) { + if (G.is_break && !mode_replay) { result = 0; break; } @@ -3582,7 +3584,7 @@ static int manta_step( update_obstacles(depsgraph, scene, ob, mds, time_per_frame, frame_length, frame, dt); /* If user requested stop, quit baking */ - if (G.is_break) { + if (G.is_break && !mode_replay) { result = 0; break; } @@ -3596,7 +3598,7 @@ static int manta_step( } /* If user requested stop, quit baking */ - if (G.is_break) { + if (G.is_break && !mode_replay) { result = 0; break; } @@ -3995,7 +3997,6 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, } } mmd->time = scene_framenr; - G.is_break = false; } static void BKE_fluid_modifier_process( -- cgit v1.2.3 From 17abae45f131a8e97871c0738ec1bac8f836679c Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 14:18:27 +0100 Subject: Multires: Cleanup, remove redundant argument Scene can be queried from the dependency graph. --- source/blender/blenkernel/BKE_multires.h | 1 - source/blender/blenkernel/intern/multires.c | 8 +++----- source/blender/blenkernel/intern/multires_reshape.c | 4 +--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index 4ee255d4e61..c3b1163cd3e 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -91,7 +91,6 @@ int multires_get_level(const struct Scene *scene, bool render, bool ignore_simplify); struct Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, - struct Scene *scene, struct MultiresModifierData *mmd, struct Object *ob); void multiresModifier_del_levels(struct MultiresModifierData *mmd, diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 4e97d0fc05c..290bed02913 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -270,13 +270,11 @@ static MDisps *multires_mdisps_initialize_hidden(Mesh *me, int level) return mdisps; } -Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, - Scene *scene, - MultiresModifierData *mmd, - Object *ob) +Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, MultiresModifierData *mmd, Object *ob) { Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - Mesh *deformed_mesh = mesh_get_eval_deform(depsgraph, scene, ob_eval, &CD_MASK_BAREMESH); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Mesh *deformed_mesh = mesh_get_eval_deform(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); ModifierEvalContext modifier_ctx = { .depsgraph = depsgraph, .object = ob_eval, diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c index a29398b24a0..657dfcec42f 100644 --- a/source/blender/blenkernel/intern/multires_reshape.c +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -107,11 +107,9 @@ bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph, highest_mmd.lvl = highest_mmd.totlvl; highest_mmd.renderlvl = highest_mmd.totlvl; - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - /* Create mesh for the multires, ignoring any further modifiers (leading * deformation modifiers will be applied though). */ - Mesh *multires_mesh = BKE_multires_create_mesh(depsgraph, scene_eval, &highest_mmd, object); + Mesh *multires_mesh = BKE_multires_create_mesh(depsgraph, &highest_mmd, object); int num_deformed_verts; float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(multires_mesh, &num_deformed_verts); -- cgit v1.2.3 From a45c34ae8eb73448726a92d0b4d79b632dc69d9e Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 14:20:14 +0100 Subject: Multires: Cleanup, argument naming and order Use full argument name. Also order arguments in the generosity order: from depsgraph (which has everything) to object (which contains multires) specific multires modifier. --- source/blender/blenkernel/BKE_multires.h | 4 ++-- source/blender/blenkernel/intern/multires.c | 11 +++++++---- source/blender/blenkernel/intern/multires_reshape.c | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index c3b1163cd3e..3ea5986a0ae 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -91,8 +91,8 @@ int multires_get_level(const struct Scene *scene, bool render, bool ignore_simplify); struct Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, - struct MultiresModifierData *mmd, - struct Object *ob); + struct Object *object, + struct MultiresModifierData *mmd); void multiresModifier_del_levels(struct MultiresModifierData *mmd, struct Scene *scene, struct Object *object, diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 290bed02913..b8464bf02c0 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -270,14 +270,17 @@ static MDisps *multires_mdisps_initialize_hidden(Mesh *me, int level) return mdisps; } -Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, MultiresModifierData *mmd, Object *ob) +Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, + Object *object, + MultiresModifierData *mmd) { - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Mesh *deformed_mesh = mesh_get_eval_deform(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); + Mesh *deformed_mesh = mesh_get_eval_deform( + depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH); ModifierEvalContext modifier_ctx = { .depsgraph = depsgraph, - .object = ob_eval, + .object = object_eval, .flag = MOD_APPLY_USECACHE | MOD_APPLY_IGNORE_SIMPLIFY, }; diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c index 657dfcec42f..6097121fefd 100644 --- a/source/blender/blenkernel/intern/multires_reshape.c +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -109,7 +109,7 @@ bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph, /* Create mesh for the multires, ignoring any further modifiers (leading * deformation modifiers will be applied though). */ - Mesh *multires_mesh = BKE_multires_create_mesh(depsgraph, &highest_mmd, object); + Mesh *multires_mesh = BKE_multires_create_mesh(depsgraph, object, &highest_mmd); int num_deformed_verts; float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(multires_mesh, &num_deformed_verts); -- cgit v1.2.3 From bf5151b2d29dc64d11c86d04485667f6045c0aed Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 15:28:24 +0100 Subject: Mesh: Add utility to calculate deform modifier up to index Intention is to be used to create mesh at the state which is an input to the multires modifier. --- source/blender/blenkernel/BKE_mesh_runtime.h | 6 ++++++ source/blender/blenkernel/intern/DerivedMesh.c | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h index fdddafcc71f..bdf7692b89b 100644 --- a/source/blender/blenkernel/BKE_mesh_runtime.h +++ b/source/blender/blenkernel/BKE_mesh_runtime.h @@ -87,6 +87,12 @@ struct Mesh *mesh_create_eval_final_view(struct Depsgraph *depsgraph, struct Object *ob, const struct CustomData_MeshMasks *dataMask); +struct Mesh *mesh_create_eval_final_view_index(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + const struct CustomData_MeshMasks *dataMask, + int index); + struct Mesh *mesh_create_eval_no_deform(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 5feb51534e9..2db48e1fb13 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2013,6 +2013,27 @@ Mesh *mesh_create_eval_final_view(Depsgraph *depsgraph, return final; } +Mesh *mesh_create_eval_final_view_index(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + const CustomData_MeshMasks *dataMask, + int index) +{ + Mesh *final; + + /* XXX hack + * psys modifier updates particle state when called during dupli-list generation, + * which can lead to wrong transforms. This disables particle system modifier execution. + */ + ob->transflag |= OB_NO_PSYS_UPDATE; + + mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, index, false, false, NULL, &final); + + ob->transflag &= ~OB_NO_PSYS_UPDATE; + + return final; +} + Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph, Scene *scene, Object *ob, -- cgit v1.2.3 From c76d390c921de4988a48a6d143a2ef08f2165a88 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 15:29:02 +0100 Subject: Mesh: Fix applying deform modifier up to index The code would have break the first (deform only) modifiers once the index is reached, but it will not prevent second loop (over remaining modifiers) from run. This was applying deform modifier twice in some conditions: having single deform modifier and calculating deformed mesh up to the first modifier (index=0). --- source/blender/blenkernel/intern/DerivedMesh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 2db48e1fb13..2113b184f01 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -981,6 +981,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* grab modifiers until index i */ if ((index != -1) && (BLI_findindex(&ob->modifiers, md) >= index)) { + md = NULL; break; } } -- cgit v1.2.3 From 628d799c85965191d5f6231b3433bbae425e59f0 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 15:55:59 +0100 Subject: Multires: Add utility to create deformed base mesh The new function will use original object as a starting point and apply all enabled deformation modifiers prior to the multires. --- source/blender/blenkernel/BKE_multires.h | 9 +++++++++ source/blender/blenkernel/intern/multires.c | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index 3ea5986a0ae..cccb5547603 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -90,9 +90,18 @@ int multires_get_level(const struct Scene *scene, const struct MultiresModifierData *mmd, bool render, bool ignore_simplify); + +/* Creates mesh with multires modifier applied on current object's deform mesh. */ struct Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, struct Object *object, struct MultiresModifierData *mmd); + +/* Creates mesh with all deform modifiers leading the multires one applied. + * NOTE: The modifiers will be re-evaluated. */ +struct Mesh *BKE_multires_create_deformed_base_mesh(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd); + void multiresModifier_del_levels(struct MultiresModifierData *mmd, struct Scene *scene, struct Object *object, diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index b8464bf02c0..fd91f80b047 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -293,6 +293,25 @@ Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, return result; } +Mesh *BKE_multires_create_deformed_base_mesh(struct Depsgraph *depsgraph, + Object *object, + MultiresModifierData *mmd) +{ + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); + + const int mmd_index = BLI_findindex(&object->modifiers, &mmd->modifier); + BLI_assert(mmd_index != -1); + + Object object_for_eval = *object_eval; + object_for_eval.data = object->data; + + Mesh *base_mesh = mesh_create_eval_final_view_index( + depsgraph, scene_eval, object, &CD_MASK_BAREMESH, mmd_index - 1); + + return base_mesh; +} + MultiresModifierData *find_multires_modifier_before(Scene *scene, ModifierData *lastmd) { ModifierData *md; -- cgit v1.2.3 From 24e44143a192053f552e4c4b1655146be3aaed61 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 16:08:31 +0100 Subject: Multires: Fix Apply Base when there are deform modifiers Their effect was applied twice after hitting Apply Base since the operator was also applying deformation caused by those modifiers. --- .../blender/blenkernel/intern/multires_reshape.c | 27 +++++++++- .../blender/blenkernel/intern/multires_reshape.h | 16 +++++- .../intern/multires_reshape_apply_base.c | 62 +++++++++++++++++++--- .../blenkernel/intern/multires_reshape_util.c | 4 ++ 4 files changed, 99 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c index 6097121fefd..53e1af15192 100644 --- a/source/blender/blenkernel/intern/multires_reshape.c +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -224,10 +224,35 @@ void multiresModifier_base_apply(struct Depsgraph *depsgraph, return; } + multires_reshape_store_original_grids(&reshape_context); + + /* At this point base_mesh is object's mesh, the subdiv is initialized to the deformed state of + * the base mesh. + * Store coordinates of top level grids in object space which will define true shape we would + * want to reshape to after modifying the base mesh. */ multires_reshape_assign_final_coords_from_mdisps(&reshape_context); + + /* For modifying base mesh we only want to consider deformation caused by multires displacement + * and ignore all deformation which might be caused by deformation modifiers leading the multires + * one. + * So refine the subdiv to the original mesh verticies positions, which will also need to make + * it so object space displacement is re-evaluated for them (as in, can not re-use any knowledge + * from the final coordinates in the object space ). */ + multires_reshape_apply_base_refine_from_base(&reshape_context); + + /* Modify original mesh coordinates. This happens in two steps: + * - Coordinates are set to their final location, where they are intended to be in the final + * result. + * - Heuristic moves them a bit, kind of canceling out the effect of subsurf (so then when + * multires modifier applies subsurf vertices are placed at the desired location). */ multires_reshape_apply_base_update_mesh_coords(&reshape_context); multires_reshape_apply_base_refit_base_mesh(&reshape_context); - multires_reshape_apply_base_refine_subdiv(&reshape_context); + + /* Reshape to the stored final state. + * Not that the base changed, so the subdiv is to be refined to the new positions. Unfortunately, + * this can not be done foe entirely cheap: if there were deformation modifiers prior to the + * multires they need to be re-evaluated for the new base mesh. */ + multires_reshape_apply_base_refine_from_deform(&reshape_context); multires_reshape_object_grids_to_tangent_displacement(&reshape_context); multires_reshape_context_free(&reshape_context); diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h index bdadc1f2800..deeb885e15a 100644 --- a/source/blender/blenkernel/intern/multires_reshape.h +++ b/source/blender/blenkernel/intern/multires_reshape.h @@ -36,6 +36,11 @@ struct Subdiv; struct SubdivCCG; typedef struct MultiresReshapeContext { + /* NOTE: Only available when context is initialized from object. */ + struct Depsgraph *depsgraph; + struct Object *object; + struct MultiresModifierData *mmd; + /* Base mesh from original object. * NOTE: Does NOT include any leading modifiers in it. */ struct Mesh *base_mesh; @@ -142,6 +147,9 @@ struct Subdiv *multires_reshape_create_subdiv(struct Depsgraph *depsgraph, struct Object *object, const struct MultiresModifierData *mmd); +/* NOTE: Initialized base mesh to object's mesh, the Subdiv is created from the deformed + * mesh prior to the multires modifier if depsgraph is not NULL. If the depsgraph is NULL + * then Subdiv is created from base mesh (without any deformation applied). */ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context, struct Depsgraph *depsgraph, struct Object *object, @@ -302,6 +310,12 @@ void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *resh void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape_context); /* Refine subdivision surface to the new positions of the base mesh. */ -void multires_reshape_apply_base_refine_subdiv(MultiresReshapeContext *reshape_context); +void multires_reshape_apply_base_refine_from_base(MultiresReshapeContext *reshape_context); + +/* Refine subdivision surface to the new positions of the deformed mesh (base mesh with all + * modifiers leading the multires applied). + * + * NOTE: Will re-evaluate all leading modifiers, so it's not cheap. */ +void multires_reshape_apply_base_refine_from_deform(MultiresReshapeContext *reshape_context); #endif /* __BKE_INTERN_MULTIRES_RESHAPE_H__ */ diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.c b/source/blender/blenkernel/intern/multires_reshape_apply_base.c index e05b5bb3179..958fb60bbdc 100644 --- a/source/blender/blenkernel/intern/multires_reshape_apply_base.c +++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.c @@ -27,23 +27,46 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" #include "BLI_math_vector.h" +#include "BLI_listbase.h" +#include "BKE_customdata.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" #include "BKE_mesh_mapping.h" +#include "BKE_multires.h" #include "BKE_subdiv_eval.h" +#include "DEG_depsgraph_query.h" + void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *reshape_context) { Mesh *base_mesh = reshape_context->base_mesh; - const int grid_size = reshape_context->top.grid_size; - const int grid_index = grid_size * grid_size - 1; - for (int i = 0; i < base_mesh->totloop; ++i) { - MDisps *displacement_grid = &reshape_context->mdisps[i]; - const MLoop *loop = &base_mesh->mloop[i]; - MVert *vert = &base_mesh->mvert[loop->v]; - copy_v3_v3(vert->co, displacement_grid->disps[grid_index]); + const MLoop *mloop = base_mesh->mloop; + MVert *mvert = base_mesh->mvert; + for (int loop_index = 0; loop_index < base_mesh->totloop; ++loop_index) { + const MLoop *loop = &mloop[loop_index]; + MVert *vert = &mvert[loop->v]; + + GridCoord grid_coord; + grid_coord.grid_index = loop_index; + grid_coord.u = 1.0f; + grid_coord.v = 1.0f; + + float P[3]; + float tangent_matrix[3][3]; + multires_reshape_evaluate_limit_at_grid(reshape_context, &grid_coord, P, tangent_matrix); + + ReshapeConstGridElement grid_element = multires_reshape_orig_grid_element_for_grid_coord( + reshape_context, &grid_coord); + float D[3]; + mul_v3_m3v3(D, tangent_matrix, grid_element.displacement); + + add_v3_v3v3(vert->co, P, D); } } @@ -152,7 +175,30 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape BKE_mesh_calc_normals(base_mesh); } -void multires_reshape_apply_base_refine_subdiv(MultiresReshapeContext *reshape_context) +void multires_reshape_apply_base_refine_from_base(MultiresReshapeContext *reshape_context) { BKE_subdiv_eval_update_from_mesh(reshape_context->subdiv, reshape_context->base_mesh, NULL); } + +void multires_reshape_apply_base_refine_from_deform(MultiresReshapeContext *reshape_context) +{ + struct Depsgraph *depsgraph = reshape_context->depsgraph; + Object *object = reshape_context->object; + MultiresModifierData *mmd = reshape_context->mmd; + BLI_assert(depsgraph != NULL); + BLI_assert(object != NULL); + BLI_assert(mmd != NULL); + + /* If there are no modifiers prior to the multires can use base mesh as it have all the updated + * vertices already. */ + if (mmd->modifier.prev == NULL) { + BKE_subdiv_eval_update_from_mesh(reshape_context->subdiv, reshape_context->base_mesh, NULL); + } + else { + /* TODO(sergey): Possible optimization is to only evaluate new verticies positions without + * construction of the entire mesh. */ + Mesh *deformed_base_mesh = BKE_multires_create_deformed_base_mesh(depsgraph, object, mmd); + BKE_subdiv_eval_update_from_mesh(reshape_context->subdiv, deformed_base_mesh, NULL); + BKE_id_free(NULL, deformed_base_mesh); + } +} diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index 175b0ee9187..5401fe2dcda 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -163,6 +163,10 @@ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Mesh *base_mesh = (Mesh *)object->data; + reshape_context->depsgraph = depsgraph; + reshape_context->object = object; + reshape_context->mmd = mmd; + reshape_context->base_mesh = base_mesh; reshape_context->subdiv = multires_reshape_create_subdiv(depsgraph, object, mmd); -- cgit v1.2.3 From d374237d43728ddc8503629cf1018befba82b81b Mon Sep 17 00:00:00 2001 From: Robert Guetzkow Date: Tue, 17 Mar 2020 16:44:29 +0100 Subject: Fix T74838: fix dereferencing of NULL in sculpt_no_multires_poll when no active object exists Fix crash when the operator search is used while no active object exists. The cause of the issue is an attempt to dereference `ob` when it is `NULL`. Therefore this patch checks the return value of `SCULPT_mode_poll()` first, to ensure that `ob` isn't `NULL`. Reviewed By: pablodp606 Maniphest Tasks: T74838 Differential Revision: https://developer.blender.org/D7156 --- source/blender/editors/sculpt_paint/sculpt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 79c4becd405..14c11523455 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -8371,9 +8371,8 @@ static void SCULPT_OT_optimize(wmOperatorType *ot) static bool sculpt_no_multires_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - if (ss && ss->pbvh && SCULPT_mode_poll(C)) { - return BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS; + if (SCULPT_mode_poll(C) && ob->sculpt && ob->sculpt->pbvh) { + return BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_GRIDS; } return false; } -- cgit v1.2.3 From 964375e36a2b2a56d12201a3a96dd4572e0a8827 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Tue, 17 Mar 2020 10:12:47 -0600 Subject: Cleanup: blenkernel proper header inclusion for BKE_ocean.h BKE_ocean.h uses the bool type without including stdbool.h counting on someone else including that before it. With D6811 enabling automatic sorting of the includes this can no longer be counted on. This changes includes stdbool.h in BKE_ocean.h so it can build without being depended on others including the right headers before it. --- source/blender/blenkernel/BKE_ocean.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h index d3ac825039d..6ce2e13cf18 100644 --- a/source/blender/blenkernel/BKE_ocean.h +++ b/source/blender/blenkernel/BKE_ocean.h @@ -17,6 +17,8 @@ #ifndef __BKE_OCEAN_H__ #define __BKE_OCEAN_H__ +#include + /** \file * \ingroup bli */ -- cgit v1.2.3 From 2518f601094bfa14474c24c3a1e640b9fca0e6e2 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 17 Mar 2020 17:09:20 +0100 Subject: GPencil: Fix Parent layer not working The parenting was using the old logic, but with new engine the draw is done using eval data. Fixed the depsgraph relationship missing with bones to get an update when the bone is transformed. Also fixed Snap cursor to Selected --- source/blender/blenkernel/intern/gpencil.c | 33 +++++++++------------- .../intern/builder/deg_builder_relations.cc | 15 ++++++++-- source/blender/editors/gpencil/gpencil_edit.c | 12 ++++---- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index c0b40721ccc..0be92b7533d 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -4034,42 +4034,35 @@ void BKE_gpencil_update_layer_parent(const Depsgraph *depsgraph, Object *ob) } bGPdata *gpd = (bGPdata *)ob->data; - bGPDspoint *pt; - int i; - float diff_mat[4][4]; float cur_mat[4][4]; LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if ((gpl->parent != NULL) && (gpl->actframe != NULL)) { - Object *ob_eval = DEG_get_evaluated_object(depsgraph, gpl->parent); - + Object *ob_parent = DEG_get_evaluated_object(depsgraph, gpl->parent); /* calculate new matrix */ if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { - invert_m4_m4(cur_mat, ob_eval->obmat); + copy_m4_m4(cur_mat, ob_parent->obmat); } else if (gpl->partype == PARBONE) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob_eval->pose, gpl->parsubstr); - if (pchan) { - float tmp_mat[4][4]; - mul_m4_m4m4(tmp_mat, ob_eval->obmat, pchan->pose_mat); - invert_m4_m4(cur_mat, tmp_mat); + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_parent->pose, gpl->parsubstr); + if (pchan != NULL) { + copy_m4_m4(cur_mat, ob->imat); + mul_m4_m4m4(cur_mat, ob_parent->obmat, pchan->pose_mat); + } + else { + unit_m4(cur_mat); } } /* only redo if any change */ if (!equals_m4m4(gpl->inverse, cur_mat)) { - - /* first apply current transformation to all strokes */ - BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat); - /* undo local object */ - sub_v3_v3(diff_mat[3], ob->obmat[3]); - LISTBASE_FOREACH (bGPDstroke *, gps, &gpl->actframe->strokes) { + bGPDspoint *pt; + int i; for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - mul_m4_v3(diff_mat, &pt->x); + mul_m4_v3(gpl->inverse, &pt->x); + mul_m4_v3(cur_mat, &pt->x); } } - /* set new parent matrix */ - copy_m4_m4(gpl->inverse, cur_mat); } } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 6791125d1e9..27d6db6a58f 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2127,9 +2127,20 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) /* Layer parenting need react to the parent object transformation. */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->parent != NULL) { - ComponentKey transform_key(&gpl->parent->id, NodeType::TRANSFORM); ComponentKey gpd_geom_key(&gpd->id, NodeType::GEOMETRY); - add_relation(transform_key, gpd_geom_key, "GPencil Parent Layer"); + + if (gpl->partype == PARBONE) { + ComponentKey bone_key(&gpl->parent->id, NodeType::BONE, gpl->parsubstr); + OperationKey armature_key( + &gpl->parent->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); + + add_relation(bone_key, gpd_geom_key, "Bone Parent"); + add_relation(armature_key, gpd_geom_key, "Armature Parent"); + } + else { + ComponentKey transform_key(&gpl->parent->id, NodeType::TRANSFORM); + add_relation(transform_key, gpd_geom_key, "GPencil Parent Layer"); + } } } break; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index f8ad34e8d14..00fc6550459 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -2597,10 +2597,11 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot) */ static bool gp_snap_poll(bContext *C) { - bGPdata *gpd = CTX_data_gpencil_data(C); ScrArea *sa = CTX_wm_area(C); + Object *ob = CTX_data_active_object(C); - return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D)); + return (ob != NULL) && (ob->type == OB_GPENCIL) && + ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D)); } /* --------------------------------- */ @@ -2775,12 +2776,13 @@ void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot) static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Object *obact = CTX_data_active_object(C); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact); + bGPdata *gpd = (bGPdata *)ob_eval->data; Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Object *obact = CTX_data_active_object(C); float *cursor = scene->cursor.location; float centroid[3] = {0.0f}; -- cgit v1.2.3 From 1504cb26b0aa4e91b5c0ec432d2702d9e9c5275f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 15 Mar 2020 20:11:53 +0100 Subject: Cleanup: process colorspace conversion with a 1D pixel array No need to assume it's 2D or 3D. --- intern/cycles/render/colorspace.cpp | 85 +++++++++++++++---------------------- intern/cycles/render/colorspace.h | 4 +- intern/cycles/render/image.cpp | 2 +- 3 files changed, 36 insertions(+), 55 deletions(-) diff --git a/intern/cycles/render/colorspace.cpp b/intern/cycles/render/colorspace.cpp index 97a9ba3f012..7684d48756f 100644 --- a/intern/cycles/render/colorspace.cpp +++ b/intern/cycles/render/colorspace.cpp @@ -262,58 +262,49 @@ template inline void cast_from_float4(T *data, float4 value) /* Slower versions for other all data types, which needs to convert to float and back. */ template -inline void processor_apply_pixels(const OCIO::Processor *processor, - T *pixels, - size_t width, - size_t height) +inline void processor_apply_pixels(const OCIO::Processor *processor, T *pixels, size_t num_pixels) { /* TODO: implement faster version for when we know the conversion * is a simple matrix transform between linear spaces. In that case * unpremultiply is not needed. */ /* Process large images in chunks to keep temporary memory requirement down. */ - size_t y_chunk_size = max(1, 16 * 1024 * 1024 / (sizeof(float4) * width)); - vector float_pixels(y_chunk_size * width); + const size_t chunk_size = std::min((size_t)(16 * 1024 * 1024), num_pixels); + vector float_pixels(chunk_size); - for (size_t y0 = 0; y0 < height; y0 += y_chunk_size) { - size_t y1 = std::min(y0 + y_chunk_size, height); - size_t i = 0; + for (size_t j = 0; j < num_pixels; j += chunk_size) { + size_t width = std::min(chunk_size, num_pixels - j); - for (size_t y = y0; y < y1; y++) { - for (size_t x = 0; x < width; x++, i++) { - float4 value = cast_to_float4(pixels + 4 * (y * width + x)); + for (size_t i = 0; i < width; i++) { + float4 value = cast_to_float4(pixels + 4 * (j + i)); - if (!(value.w <= 0.0f || value.w == 1.0f)) { - float inv_alpha = 1.0f / value.w; - value.x *= inv_alpha; - value.y *= inv_alpha; - value.z *= inv_alpha; - } - - float_pixels[i] = value; + if (!(value.w <= 0.0f || value.w == 1.0f)) { + float inv_alpha = 1.0f / value.w; + value.x *= inv_alpha; + value.y *= inv_alpha; + value.z *= inv_alpha; } + + float_pixels[i] = value; } - OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, y_chunk_size, 4); + OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, 1, 4); processor->apply(desc); - i = 0; - for (size_t y = y0; y < y1; y++) { - for (size_t x = 0; x < width; x++, i++) { - float4 value = float_pixels[i]; + for (size_t i = 0; i < width; i++) { + float4 value = float_pixels[i]; - if (compress_as_srgb) { - value = color_linear_to_srgb_v4(value); - } - - if (!(value.w <= 0.0f || value.w == 1.0f)) { - value.x *= value.w; - value.y *= value.w; - value.z *= value.w; - } + if (compress_as_srgb) { + value = color_linear_to_srgb_v4(value); + } - cast_from_float4(pixels + 4 * (y * width + x), value); + if (!(value.w <= 0.0f || value.w == 1.0f)) { + value.x *= value.w; + value.y *= value.w; + value.z *= value.w; } + + cast_from_float4(pixels + 4 * (j + i), value); } } } @@ -322,9 +313,7 @@ inline void processor_apply_pixels(const OCIO::Processor *processor, template void ColorSpaceManager::to_scene_linear(ustring colorspace, T *pixels, - size_t width, - size_t height, - size_t depth, + size_t num_pixels, bool compress_as_srgb) { #ifdef WITH_OCIO @@ -333,23 +322,17 @@ void ColorSpaceManager::to_scene_linear(ustring colorspace, if (processor) { if (compress_as_srgb) { /* Compress output as sRGB. */ - for (size_t z = 0; z < depth; z++) { - processor_apply_pixels(processor, &pixels[z * width * height], width, height); - } + processor_apply_pixels(processor, pixels, num_pixels); } else { /* Write output as scene linear directly. */ - for (size_t z = 0; z < depth; z++) { - processor_apply_pixels(processor, &pixels[z * width * height], width, height); - } + processor_apply_pixels(processor, pixels, num_pixels); } } #else (void)colorspace; (void)pixels; - (void)width; - (void)height; - (void)depth; + (void)num_pixels; (void)compress_as_srgb; #endif } @@ -404,9 +387,9 @@ void ColorSpaceManager::free_memory() } /* Template instanstations so we don't have to inline functions. */ -template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, size_t, size_t, bool); -template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, size_t, size_t, bool); -template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, size_t, size_t, bool); -template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, size_t, size_t, bool); +template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, bool); +template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, bool); +template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, bool); +template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, bool); CCL_NAMESPACE_END diff --git a/intern/cycles/render/colorspace.h b/intern/cycles/render/colorspace.h index 9fea2d6efc6..51d0b121cc0 100644 --- a/intern/cycles/render/colorspace.h +++ b/intern/cycles/render/colorspace.h @@ -45,9 +45,7 @@ class ColorSpaceManager { template static void to_scene_linear(ustring colorspace, T *pixels, - size_t width, - size_t height, - size_t depth, + size_t num_pixels, bool compress_as_srgb); /* Efficiently convert pixels to scene linear colorspace at render time, diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 8868811b5e7..df8226fb79f 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -534,7 +534,7 @@ bool ImageManager::file_load_image(Image *img, int texture_limit) img->metadata.colorspace != u_colorspace_srgb) { /* Convert to scene linear. */ ColorSpaceManager::to_scene_linear( - img->metadata.colorspace, pixels, width, height, depth, img->metadata.compress_as_srgb); + img->metadata.colorspace, pixels, num_pixels, img->metadata.compress_as_srgb); } } -- cgit v1.2.3 From f958560a990a8974446c2c63def7ba387dcfb275 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 17:34:04 +0100 Subject: Multires: Properly support virtual modifiers for Apply Base The initial code from earlier from today didn't really work reliable since it is not possible to apply virtual modifiers but not the real multires one (in a situation like mesh with shapekeys and multires). New code uses less memory and has better performance for the case when there are actual modifiers leading the multires. The case when there is only multires will not be as performant as possible at this moment. --- source/blender/blenkernel/BKE_multires.h | 9 ++-- source/blender/blenkernel/intern/multires.c | 50 ++++++++++++++++++---- .../intern/multires_reshape_apply_base.c | 19 +++----- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index cccb5547603..a7a5e7b8bd5 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -96,11 +96,12 @@ struct Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, struct Object *object, struct MultiresModifierData *mmd); -/* Creates mesh with all deform modifiers leading the multires one applied. +/* Get coordinates of a deformed base mesh which is an ionput to the given multires modifier. * NOTE: The modifiers will be re-evaluated. */ -struct Mesh *BKE_multires_create_deformed_base_mesh(struct Depsgraph *depsgraph, - struct Object *object, - struct MultiresModifierData *mmd); +float (*BKE_multires_create_deformed_base_mesh_vert_coords(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd, + int *r_num_deformed_verts))[3]; void multiresModifier_del_levels(struct MultiresModifierData *mmd, struct Scene *scene, diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index fd91f80b047..234273b5158 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -293,23 +293,55 @@ Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph, return result; } -Mesh *BKE_multires_create_deformed_base_mesh(struct Depsgraph *depsgraph, - Object *object, - MultiresModifierData *mmd) +float (*BKE_multires_create_deformed_base_mesh_vert_coords(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd, + int *r_num_deformed_verts))[3] { Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - const int mmd_index = BLI_findindex(&object->modifiers, &mmd->modifier); - BLI_assert(mmd_index != -1); - Object object_for_eval = *object_eval; object_for_eval.data = object->data; - Mesh *base_mesh = mesh_create_eval_final_view_index( - depsgraph, scene_eval, object, &CD_MASK_BAREMESH, mmd_index - 1); + const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); + ModifierEvalContext mesh_eval_context = {depsgraph, &object_for_eval, 0}; + if (use_render) { + mesh_eval_context.flag |= MOD_APPLY_RENDER; + } + const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; + + VirtualModifierData virtual_modifier_data; + ModifierData *first_md = modifiers_getVirtualModifierList(&object_for_eval, + &virtual_modifier_data); + + Mesh *base_mesh = object->data; + + int num_deformed_verts; + float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(base_mesh, &num_deformed_verts); + + for (ModifierData *md = first_md; md != NULL; md = md->next) { + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); + + if (md == &mmd->modifier) { + break; + } + + if (!modifier_isEnabled(scene_eval, md, required_mode)) { + continue; + } - return base_mesh; + if (mti->type != eModifierTypeType_OnlyDeform) { + break; + } + + modwrap_deformVerts(md, &mesh_eval_context, base_mesh, deformed_verts, num_deformed_verts); + } + + if (r_num_deformed_verts != NULL) { + *r_num_deformed_verts = num_deformed_verts; + } + return deformed_verts; } MultiresModifierData *find_multires_modifier_before(Scene *scene, ModifierData *lastmd) diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.c b/source/blender/blenkernel/intern/multires_reshape_apply_base.c index 958fb60bbdc..62418150ff1 100644 --- a/source/blender/blenkernel/intern/multires_reshape_apply_base.c +++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.c @@ -189,16 +189,11 @@ void multires_reshape_apply_base_refine_from_deform(MultiresReshapeContext *resh BLI_assert(object != NULL); BLI_assert(mmd != NULL); - /* If there are no modifiers prior to the multires can use base mesh as it have all the updated - * vertices already. */ - if (mmd->modifier.prev == NULL) { - BKE_subdiv_eval_update_from_mesh(reshape_context->subdiv, reshape_context->base_mesh, NULL); - } - else { - /* TODO(sergey): Possible optimization is to only evaluate new verticies positions without - * construction of the entire mesh. */ - Mesh *deformed_base_mesh = BKE_multires_create_deformed_base_mesh(depsgraph, object, mmd); - BKE_subdiv_eval_update_from_mesh(reshape_context->subdiv, deformed_base_mesh, NULL); - BKE_id_free(NULL, deformed_base_mesh); - } + float(*deformed_verts)[3] = BKE_multires_create_deformed_base_mesh_vert_coords( + depsgraph, object, mmd, NULL); + + BKE_subdiv_eval_update_from_mesh( + reshape_context->subdiv, reshape_context->base_mesh, deformed_verts); + + MEM_freeN(deformed_verts); } -- cgit v1.2.3 From 6a0ddb4bb13b76ead8b3e55ca2b212cde353cac7 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 17:40:24 +0100 Subject: Cleanup: Remove unused function Was introduced earlier today and did not turn out to be very useful and clear. --- source/blender/blenkernel/BKE_mesh_runtime.h | 6 ------ source/blender/blenkernel/intern/DerivedMesh.c | 21 --------------------- 2 files changed, 27 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h index bdf7692b89b..fdddafcc71f 100644 --- a/source/blender/blenkernel/BKE_mesh_runtime.h +++ b/source/blender/blenkernel/BKE_mesh_runtime.h @@ -87,12 +87,6 @@ struct Mesh *mesh_create_eval_final_view(struct Depsgraph *depsgraph, struct Object *ob, const struct CustomData_MeshMasks *dataMask); -struct Mesh *mesh_create_eval_final_view_index(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - const struct CustomData_MeshMasks *dataMask, - int index); - struct Mesh *mesh_create_eval_no_deform(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 2113b184f01..f51c792f474 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2014,27 +2014,6 @@ Mesh *mesh_create_eval_final_view(Depsgraph *depsgraph, return final; } -Mesh *mesh_create_eval_final_view_index(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - const CustomData_MeshMasks *dataMask, - int index) -{ - Mesh *final; - - /* XXX hack - * psys modifier updates particle state when called during dupli-list generation, - * which can lead to wrong transforms. This disables particle system modifier execution. - */ - ob->transflag |= OB_NO_PSYS_UPDATE; - - mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, index, false, false, NULL, &final); - - ob->transflag &= ~OB_NO_PSYS_UPDATE; - - return final; -} - Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph, Scene *scene, Object *ob, -- cgit v1.2.3 From 07d5b8b0231f45ec2cab8391074c7635478983d5 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 17 Mar 2020 17:48:07 +0100 Subject: Fix T74322: Wrong object bundles with scaled camera Camera scale was not handled correctly when drawing 3d bundles for reconstructed objects (caused by normalization of the matrix). --- source/blender/draw/engines/overlay/overlay_extra.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 49f266291da..53550fb115e 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -875,11 +875,9 @@ static void camera_view3d_reconstruction(OVERLAY_ExtraCallBuffers *cb, UI_GetThemeColor4ubv(TH_SELECT, text_color_selected); UI_GetThemeColor4ubv(TH_TEXT, text_color_unselected); - float camera_mat[4][4], normal_mat[4][4]; + float camera_mat[4][4]; BKE_tracking_get_camera_object_matrix(ob, camera_mat); - normalize_m4_m4(normal_mat, ob->obmat); - LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) { float tracking_object_mat[4][4]; @@ -889,12 +887,15 @@ static void camera_view3d_reconstruction(OVERLAY_ExtraCallBuffers *cb, else { const int framenr = BKE_movieclip_remap_scene_to_clip_frame( clip, DEG_get_ctime(draw_ctx->depsgraph)); + float object_mat[4][4]; BKE_tracking_camera_get_reconstructed_interpolate( tracking, tracking_object, framenr, object_mat); - invert_m4(object_mat); - mul_m4_m4m4(tracking_object_mat, normal_mat, object_mat); + float object_imat[4][4]; + invert_m4_m4(object_imat, object_mat); + + mul_m4_m4m4(tracking_object_mat, ob->obmat, object_imat); } ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); -- cgit v1.2.3 From bf9c4af9bb7436468eaa4fc954ab06c7369738d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Barschkis?= Date: Tue, 17 Mar 2020 18:20:08 +0100 Subject: Fix T74762: Mantaflow: Non emmiting flow source affects simulation --- source/blender/blenkernel/intern/fluid.c | 68 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 797e03e015a..dc872b933eb 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1894,40 +1894,6 @@ static void sample_mesh(FluidFlowSettings *mfs, v3 = mloop[mlooptri[f_index].tri[2]].v; interp_weights_tri_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, nearest.co); - /* Initial velocity of flow object. */ - if (mfs->flags & FLUID_FLOW_INITVELOCITY && velocity_map) { - /* Apply normal directional velocity. */ - if (mfs->vel_normal) { - /* Interpolate vertex normal vectors to get nearest point normal. */ - normal_short_to_float_v3(n1, mvert[v1].no); - normal_short_to_float_v3(n2, mvert[v2].no); - normal_short_to_float_v3(n3, mvert[v3].no); - interp_v3_v3v3v3(hit_normal, n1, n2, n3, weights); - normalize_v3(hit_normal); - - /* Apply normal directional velocity. */ - velocity_map[index * 3] += hit_normal[0] * mfs->vel_normal * 0.25f; - velocity_map[index * 3 + 1] += hit_normal[1] * mfs->vel_normal * 0.25f; - velocity_map[index * 3 + 2] += hit_normal[2] * mfs->vel_normal * 0.25f; - } - /* Apply object velocity. */ - if (has_velocity && mfs->vel_multi) { - float hit_vel[3]; - interp_v3_v3v3v3( - hit_vel, &vert_vel[v1 * 3], &vert_vel[v2 * 3], &vert_vel[v3 * 3], weights); - velocity_map[index * 3] += hit_vel[0] * mfs->vel_multi; - velocity_map[index * 3 + 1] += hit_vel[1] * mfs->vel_multi; - velocity_map[index * 3 + 2] += hit_vel[2] * mfs->vel_multi; -# ifdef DEBUG_PRINT - /* Debugging: Print flow object velocities. */ - printf("adding flow object vel: [%f, %f, %f]\n", hit_vel[0], hit_vel[1], hit_vel[2]); -# endif - } - velocity_map[index * 3] += mfs->vel_coord[0]; - velocity_map[index * 3 + 1] += mfs->vel_coord[1]; - velocity_map[index * 3 + 2] += mfs->vel_coord[2]; - } - /* Compute emission strength for smoke flow. */ if (is_gas_flow) { /* Emission from surface is based on UI configurable distance value. */ @@ -1977,6 +1943,40 @@ static void sample_mesh(FluidFlowSettings *mfs, emission_strength *= texres.tin; } } + + /* Initial velocity of flow object. Only compute velocity if emission is present. */ + if (mfs->flags & FLUID_FLOW_INITVELOCITY && velocity_map && emission_strength != 0.0) { + /* Apply normal directional velocity. */ + if (mfs->vel_normal) { + /* Interpolate vertex normal vectors to get nearest point normal. */ + normal_short_to_float_v3(n1, mvert[v1].no); + normal_short_to_float_v3(n2, mvert[v2].no); + normal_short_to_float_v3(n3, mvert[v3].no); + interp_v3_v3v3v3(hit_normal, n1, n2, n3, weights); + normalize_v3(hit_normal); + + /* Apply normal directional velocity. */ + velocity_map[index * 3] += hit_normal[0] * mfs->vel_normal * 0.25f; + velocity_map[index * 3 + 1] += hit_normal[1] * mfs->vel_normal * 0.25f; + velocity_map[index * 3 + 2] += hit_normal[2] * mfs->vel_normal * 0.25f; + } + /* Apply object velocity. */ + if (has_velocity && mfs->vel_multi) { + float hit_vel[3]; + interp_v3_v3v3v3( + hit_vel, &vert_vel[v1 * 3], &vert_vel[v2 * 3], &vert_vel[v3 * 3], weights); + velocity_map[index * 3] += hit_vel[0] * mfs->vel_multi; + velocity_map[index * 3 + 1] += hit_vel[1] * mfs->vel_multi; + velocity_map[index * 3 + 2] += hit_vel[2] * mfs->vel_multi; +# ifdef DEBUG_PRINT + /* Debugging: Print flow object velocities. */ + printf("adding flow object vel: [%f, %f, %f]\n", hit_vel[0], hit_vel[1], hit_vel[2]); +# endif + } + velocity_map[index * 3] += mfs->vel_coord[0]; + velocity_map[index * 3 + 1] += mfs->vel_coord[1]; + velocity_map[index * 3 + 2] += mfs->vel_coord[2]; + } } /* Apply final influence value but also consider volume initialization factor. */ -- cgit v1.2.3 From f1eb86c458f256d983fbb553be07b509dbdd0714 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 17 Mar 2020 18:29:18 +0100 Subject: GPencil: Rename old color operators to material The color was used in old version when palettes were used, but now all are materials --- .../bl_ui/properties_grease_pencil_common.py | 6 +- .../startup/bl_ui/properties_material_gpencil.py | 14 ++-- source/blender/editors/gpencil/gpencil_data.c | 88 +++++++++++----------- source/blender/editors/gpencil/gpencil_intern.h | 12 +-- source/blender/editors/gpencil/gpencil_ops.c | 12 +-- 5 files changed, 66 insertions(+), 66 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 95997b48680..64d4b6e2d4a 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -605,7 +605,7 @@ class GreasePencilMaterialsPanel: col.separator() - col.menu("GPENCIL_MT_color_context_menu", icon='DOWNARROW_HLT', text="") + col.menu("GPENCIL_MT_material_context_menu", icon='DOWNARROW_HLT', text="") if is_sortable: col.separator() @@ -616,8 +616,8 @@ class GreasePencilMaterialsPanel: col.separator() sub = col.column(align=True) - sub.operator("gpencil.color_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True - sub.operator("gpencil.color_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.material_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True + sub.operator("gpencil.material_isolate", icon='LOCKED', text="").affect_visibility = False if show_full_ui: row = layout.row() diff --git a/release/scripts/startup/bl_ui/properties_material_gpencil.py b/release/scripts/startup/bl_ui/properties_material_gpencil.py index 56201b29e7f..6dff706e839 100644 --- a/release/scripts/startup/bl_ui/properties_material_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_material_gpencil.py @@ -27,21 +27,21 @@ from bl_ui.properties_grease_pencil_common import ( ) -class GPENCIL_MT_color_context_menu(Menu): +class GPENCIL_MT_material_context_menu(Menu): bl_label = "Material Specials" def draw(self, _context): layout = self.layout - layout.operator("gpencil.color_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") - layout.operator("gpencil.color_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True + layout.operator("gpencil.material_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") + layout.operator("gpencil.material_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True layout.separator() - layout.operator("gpencil.color_lock_all", icon='LOCKED', text="Lock All") - layout.operator("gpencil.color_unlock_all", icon='UNLOCKED', text="UnLock All") + layout.operator("gpencil.material_lock_all", icon='LOCKED', text="Lock All") + layout.operator("gpencil.material_unlock_all", icon='UNLOCKED', text="UnLock All") - layout.operator("gpencil.stroke_lock_color", text="Lock Unselected") + layout.operator("gpencil.material_lock_unused", text="Lock Unselected") layout.operator("gpencil.lock_layer", text="Lock Unused") layout.separator() @@ -261,7 +261,7 @@ class MATERIAL_PT_gpencil_material_presets(PresetPanel, Panel): classes = ( GPENCIL_UL_matslots, - GPENCIL_MT_color_context_menu, + GPENCIL_MT_material_context_menu, MATERIAL_PT_gpencil_slots, MATERIAL_PT_gpencil_preview, MATERIAL_PT_gpencil_material_presets, diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index e5d332a86de..13e1be329c7 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -1599,7 +1599,7 @@ void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) /* ******************* Lock color of non selected Strokes colors ************************** */ -static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) +static int gp_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); @@ -1655,15 +1655,15 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) +void GPENCIL_OT_material_lock_unused(wmOperatorType *ot) { /* identifiers */ - ot->name = "Lock Unused Colors"; - ot->idname = "GPENCIL_OT_stroke_lock_color"; - ot->description = "Lock any color not used in any selected stroke"; + ot->name = "Lock Unused Materials"; + ot->idname = "GPENCIL_OT_material_lock_unused"; + ot->description = "Lock any material not used in any selected stroke"; /* api callbacks */ - ot->exec = gp_stroke_lock_color_exec; + ot->exec = gp_material_lock_unsused_exec; ot->poll = gp_active_layer_poll; /* flags */ @@ -2757,7 +2757,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) } /* Color Handle operator */ -static bool gpencil_active_color_poll(bContext *C) +static bool gpencil_active_material_poll(bContext *C) { Object *ob = CTX_data_active_object(C); if (ob && ob->data && (ob->type == OB_GPENCIL)) { @@ -2847,7 +2847,7 @@ void GPENCIL_OT_lock_layer(wmOperatorType *ot) /* ********************** Isolate gpencil_ color **************************** */ -static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) +static int gpencil_material_isolate_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Object *ob = CTX_data_active_object(C); @@ -2929,17 +2929,17 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GPENCIL_OT_color_isolate(wmOperatorType *ot) +void GPENCIL_OT_material_isolate(wmOperatorType *ot) { /* identifiers */ - ot->name = "Isolate Color"; - ot->idname = "GPENCIL_OT_color_isolate"; + ot->name = "Isolate Material"; + ot->idname = "GPENCIL_OT_material_isolate"; ot->description = - "Toggle whether the active color is the only one that is editable and/or visible"; + "Toggle whether the active material is the only one that is editable and/or visible"; /* callbacks */ - ot->exec = gpencil_color_isolate_exec; - ot->poll = gpencil_active_color_poll; + ot->exec = gpencil_material_isolate_exec; + ot->poll = gpencil_active_material_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2955,7 +2955,7 @@ void GPENCIL_OT_color_isolate(wmOperatorType *ot) /* *********************** Hide colors ******************************** */ -static int gpencil_color_hide_exec(bContext *C, wmOperator *op) +static int gpencil_material_hide_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; @@ -3000,16 +3000,16 @@ static int gpencil_color_hide_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GPENCIL_OT_color_hide(wmOperatorType *ot) +void GPENCIL_OT_material_hide(wmOperatorType *ot) { /* identifiers */ - ot->name = "Hide Color(s)"; - ot->idname = "GPENCIL_OT_color_hide"; - ot->description = "Hide selected/unselected Grease Pencil colors"; + ot->name = "Hide Material(s)"; + ot->idname = "GPENCIL_OT_material_hide"; + ot->description = "Hide selected/unselected Grease Pencil materials"; /* callbacks */ - ot->exec = gpencil_color_hide_exec; - ot->poll = gpencil_active_color_poll; /* NOTE: we need an active color to play with */ + ot->exec = gpencil_material_hide_exec; + ot->poll = gpencil_active_material_poll; /* NOTE: we need an active color to play with */ /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -3021,7 +3021,7 @@ void GPENCIL_OT_color_hide(wmOperatorType *ot) /* ********************** Show All Colors ***************************** */ -static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_material_reveal_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; @@ -3056,16 +3056,16 @@ static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_color_reveal(wmOperatorType *ot) +void GPENCIL_OT_material_reveal(wmOperatorType *ot) { /* identifiers */ - ot->name = "Show All Colors"; - ot->idname = "GPENCIL_OT_color_reveal"; - ot->description = "Unhide all hidden Grease Pencil colors"; + ot->name = "Show All Materials"; + ot->idname = "GPENCIL_OT_material_reveal"; + ot->description = "Unhide all hidden Grease Pencil materials"; /* callbacks */ - ot->exec = gpencil_color_reveal_exec; - ot->poll = gpencil_active_color_poll; + ot->exec = gpencil_material_reveal_exec; + ot->poll = gpencil_active_material_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -3073,7 +3073,7 @@ void GPENCIL_OT_color_reveal(wmOperatorType *ot) /* ***************** Lock/Unlock All colors ************************ */ -static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_material_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = CTX_data_active_object(C); @@ -3109,17 +3109,17 @@ static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_color_lock_all(wmOperatorType *ot) +void GPENCIL_OT_material_lock_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Lock All Colors"; - ot->idname = "GPENCIL_OT_color_lock_all"; + ot->name = "Lock All Materials"; + ot->idname = "GPENCIL_OT_material_lock_all"; ot->description = - "Lock all Grease Pencil colors to prevent them from being accidentally modified"; + "Lock all Grease Pencil materials to prevent them from being accidentally modified"; /* callbacks */ - ot->exec = gpencil_color_lock_all_exec; - ot->poll = gpencil_active_color_poll; + ot->exec = gpencil_material_lock_all_exec; + ot->poll = gpencil_active_material_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -3127,7 +3127,7 @@ void GPENCIL_OT_color_lock_all(wmOperatorType *ot) /* -------------------------- */ -static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_material_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; @@ -3162,16 +3162,16 @@ static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_color_unlock_all(wmOperatorType *ot) +void GPENCIL_OT_material_unlock_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Unlock All Colors"; - ot->idname = "GPENCIL_OT_color_unlock_all"; - ot->description = "Unlock all Grease Pencil colors so that they can be edited"; + ot->name = "Unlock All Materials"; + ot->idname = "GPENCIL_OT_material_unlock_all"; + ot->description = "Unlock all Grease Pencil materials so that they can be edited"; /* callbacks */ - ot->exec = gpencil_color_unlock_all_exec; - ot->poll = gpencil_active_color_poll; + ot->exec = gpencil_material_unlock_all_exec; + ot->poll = gpencil_active_material_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -3258,7 +3258,7 @@ void GPENCIL_OT_select_material(wmOperatorType *ot) /* callbacks */ ot->exec = gpencil_select_material_exec; - ot->poll = gpencil_active_color_poll; + ot->poll = gpencil_active_material_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -3310,7 +3310,7 @@ void GPENCIL_OT_set_active_material(wmOperatorType *ot) /* callbacks */ ot->exec = gpencil_set_active_material_exec; - ot->poll = gpencil_active_color_poll; + ot->poll = gpencil_active_material_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index a12fd81e032..f934a5c74f3 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -491,7 +491,6 @@ enum { void GPENCIL_OT_stroke_arrange(struct wmOperatorType *ot); void GPENCIL_OT_stroke_change_color(struct wmOperatorType *ot); -void GPENCIL_OT_stroke_lock_color(struct wmOperatorType *ot); void GPENCIL_OT_stroke_apply_thickness(struct wmOperatorType *ot); void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot); void GPENCIL_OT_stroke_caps_set(struct wmOperatorType *ot); @@ -547,11 +546,12 @@ void GPENCIL_OT_vertex_group_normalize_all(struct wmOperatorType *ot); /* color handle */ void GPENCIL_OT_lock_layer(struct wmOperatorType *ot); -void GPENCIL_OT_color_isolate(struct wmOperatorType *ot); -void GPENCIL_OT_color_hide(struct wmOperatorType *ot); -void GPENCIL_OT_color_reveal(struct wmOperatorType *ot); -void GPENCIL_OT_color_lock_all(struct wmOperatorType *ot); -void GPENCIL_OT_color_unlock_all(struct wmOperatorType *ot); +void GPENCIL_OT_material_isolate(struct wmOperatorType *ot); +void GPENCIL_OT_material_hide(struct wmOperatorType *ot); +void GPENCIL_OT_material_reveal(struct wmOperatorType *ot); +void GPENCIL_OT_material_lock_all(struct wmOperatorType *ot); +void GPENCIL_OT_material_unlock_all(struct wmOperatorType *ot); +void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot); void GPENCIL_OT_select_material(struct wmOperatorType *ot); void GPENCIL_OT_set_active_material(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 233bebfc9b6..8794a27fb96 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -608,7 +608,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_arrange); WM_operatortype_append(GPENCIL_OT_stroke_change_color); - WM_operatortype_append(GPENCIL_OT_stroke_lock_color); + WM_operatortype_append(GPENCIL_OT_material_lock_unused); WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness); WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set); WM_operatortype_append(GPENCIL_OT_stroke_caps_set); @@ -648,11 +648,11 @@ void ED_operatortypes_gpencil(void) /* color handle */ WM_operatortype_append(GPENCIL_OT_lock_layer); - WM_operatortype_append(GPENCIL_OT_color_isolate); - WM_operatortype_append(GPENCIL_OT_color_hide); - WM_operatortype_append(GPENCIL_OT_color_reveal); - WM_operatortype_append(GPENCIL_OT_color_lock_all); - WM_operatortype_append(GPENCIL_OT_color_unlock_all); + WM_operatortype_append(GPENCIL_OT_material_isolate); + WM_operatortype_append(GPENCIL_OT_material_hide); + WM_operatortype_append(GPENCIL_OT_material_reveal); + WM_operatortype_append(GPENCIL_OT_material_lock_all); + WM_operatortype_append(GPENCIL_OT_material_unlock_all); WM_operatortype_append(GPENCIL_OT_select_material); /* Editing (Time) --------------- */ -- cgit v1.2.3 From ba3d49225c9ff3514fb87ae5d692baefe5edec30 Mon Sep 17 00:00:00 2001 From: blender Date: Tue, 17 Mar 2020 18:32:50 +0100 Subject: make deps: Fixes to make OpenXR to work on CentOS Linux - Harvest to a proper location. - Disable STD's filesystem which is experimental and caused linking errors when OpenXR is usedi n Blender. --- build_files/build_environment/cmake/harvest.cmake | 2 +- build_files/build_environment/cmake/xr_openxr.cmake | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake index 6801ef284d0..4d27509890f 100644 --- a/build_files/build_environment/cmake/harvest.cmake +++ b/build_files/build_environment/cmake/harvest.cmake @@ -162,7 +162,7 @@ harvest(opensubdiv/lib opensubdiv/lib "*.a") harvest(openvdb/include/openvdb openvdb/include/openvdb "*.h") harvest(openvdb/lib openvdb/lib "*.a") harvest(xr_openxr_sdk/include/openxr xr_openxr_sdk/include/openxr "*.h") -harvest(xr_openxr_sdk/lib xr_openxr_sdk/src/loader "*.a") +harvest(xr_openxr_sdk/lib xr_openxr_sdk/lib "*.a") harvest(osl/bin osl/bin "oslc") harvest(osl/include osl/include "*.h") harvest(osl/lib osl/lib "*.a") diff --git a/build_files/build_environment/cmake/xr_openxr.cmake b/build_files/build_environment/cmake/xr_openxr.cmake index b0751e418b9..3eb613e3a42 100644 --- a/build_files/build_environment/cmake/xr_openxr.cmake +++ b/build_files/build_environment/cmake/xr_openxr.cmake @@ -29,6 +29,7 @@ if(UNIX AND NOT APPLE) -DBUILD_WITH_WAYLAND_HEADERS=OFF -DBUILD_WITH_XCB_HEADERS=OFF -DBUILD_WITH_XLIB_HEADERS=ON + -DCMAKE_CXX_FLAGS=-DDISABLE_STD_FILESYSTEM=1 ) endif() -- cgit v1.2.3 From e09f0caff32e61095c3349daa394e1a45b18a893 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 17 Mar 2020 19:01:51 +0100 Subject: GPencil: Fix crash joining objects The weights array can be NULL. --- source/blender/editors/gpencil/gpencil_data.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 13e1be329c7..7ba4637f9d8 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -2627,8 +2627,11 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { MDeformVert *dvert; int i; + if (gps->dvert == NULL) { + continue; + } for (i = 0, dvert = gps->dvert; i < gps->totpoints; i++, dvert++) { - if ((dvert->dw) && (dvert->dw->def_nr == old_idx)) { + if ((dvert->dw != NULL) && (dvert->dw->def_nr == old_idx)) { dvert->dw->def_nr = idx; } } -- cgit v1.2.3 From c333f5760335cd11aa1c3319df39fa3d4b010aa5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 17 Mar 2020 20:10:57 +0100 Subject: Ghost: Ghost-XR API to abstract away and access OpenXR functionality Extends Ghost to include an abstraction for OpenXR, which I refer to as Ghost-XR. Such an API is the base for the following commit, which introduces VR support to Blender. Main features: * Simple and high-level interface for Blender specific code to call. * Extensible for muliple graphics backends, currently OpenGL and a DirectX compatibility layer are supported. * Carefully designed error handling strategy allowing Blender to handle errors gracefully and with useful error messages. * OpenXR extension and API-layer management. * OpenXR session management. * Basic OpenXR event management. * Debug utilities for Ghost-XR and OpenXR For more information on this API, check https://wiki.blender.org/wiki/Source/Interface/XR. Reviewed by: Brecht Van Lommel Differential Revision: https://developer.blender.org/D6188 --- intern/ghost/CMakeLists.txt | 44 ++ intern/ghost/GHOST_C-api.h | 100 ++++- intern/ghost/GHOST_IXrContext.h | 42 ++ intern/ghost/GHOST_Types.h | 90 ++++ intern/ghost/intern/GHOST_C-api.cpp | 64 +++ intern/ghost/intern/GHOST_ContextD3D.h | 3 + intern/ghost/intern/GHOST_ContextGLX.cpp | 1 + intern/ghost/intern/GHOST_ContextGLX.h | 3 + intern/ghost/intern/GHOST_ContextWGL.h | 3 + intern/ghost/intern/GHOST_IXrGraphicsBinding.h | 71 +++ intern/ghost/intern/GHOST_Xr.cpp | 59 +++ intern/ghost/intern/GHOST_XrContext.cpp | 550 ++++++++++++++++++++++++ intern/ghost/intern/GHOST_XrContext.h | 127 ++++++ intern/ghost/intern/GHOST_XrEvent.cpp | 64 +++ intern/ghost/intern/GHOST_XrException.h | 45 ++ intern/ghost/intern/GHOST_XrGraphicsBinding.cpp | 316 ++++++++++++++ intern/ghost/intern/GHOST_XrSession.cpp | 487 +++++++++++++++++++++ intern/ghost/intern/GHOST_XrSession.h | 83 ++++ intern/ghost/intern/GHOST_XrSwapchain.cpp | 131 ++++++ intern/ghost/intern/GHOST_XrSwapchain.h | 45 ++ intern/ghost/intern/GHOST_Xr_intern.h | 50 +++ intern/ghost/intern/GHOST_Xr_openxr_includes.h | 52 +++ 22 files changed, 2415 insertions(+), 15 deletions(-) create mode 100644 intern/ghost/GHOST_IXrContext.h create mode 100644 intern/ghost/intern/GHOST_IXrGraphicsBinding.h create mode 100644 intern/ghost/intern/GHOST_Xr.cpp create mode 100644 intern/ghost/intern/GHOST_XrContext.cpp create mode 100644 intern/ghost/intern/GHOST_XrContext.h create mode 100644 intern/ghost/intern/GHOST_XrEvent.cpp create mode 100644 intern/ghost/intern/GHOST_XrException.h create mode 100644 intern/ghost/intern/GHOST_XrGraphicsBinding.cpp create mode 100644 intern/ghost/intern/GHOST_XrSession.cpp create mode 100644 intern/ghost/intern/GHOST_XrSession.h create mode 100644 intern/ghost/intern/GHOST_XrSwapchain.cpp create mode 100644 intern/ghost/intern/GHOST_XrSwapchain.h create mode 100644 intern/ghost/intern/GHOST_Xr_intern.h create mode 100644 intern/ghost/intern/GHOST_Xr_openxr_includes.h diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index c4f1efe1580..07d98475c00 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -357,6 +357,50 @@ elseif(WIN32) endif() +if(WITH_XR_OPENXR) + list(APPEND SRC + intern/GHOST_Xr.cpp + intern/GHOST_XrContext.cpp + intern/GHOST_XrEvent.cpp + intern/GHOST_XrGraphicsBinding.cpp + intern/GHOST_XrSession.cpp + intern/GHOST_XrSwapchain.cpp + + GHOST_IXrContext.h + intern/GHOST_IXrGraphicsBinding.h + intern/GHOST_Xr_intern.h + intern/GHOST_Xr_openxr_includes.h + intern/GHOST_XrContext.h + intern/GHOST_XrSession.h + intern/GHOST_XrSwapchain.h + ) + list(APPEND INC_SYS + ${XR_OPENXR_SDK_INCLUDE_DIR} + ) + + set(XR_PLATFORM_DEFINES -DXR_USE_GRAPHICS_API_OPENGL) + + # Add compiler defines as required by the OpenXR specification. + if(WIN32) + list(APPEND XR_PLATFORM_DEFINES + -DXR_USE_PLATFORM_WIN32 + -DXR_USE_GRAPHICS_API_D3D11 + ) + list(APPEND LIB + shlwapi + ) + elseif(UNIX AND NOT APPLE) + list(APPEND XR_PLATFORM_DEFINES + -DXR_OS_LINUX + -DXR_USE_PLATFORM_XLIB + ) + endif() + + add_definitions(-DWITH_XR_OPENXR ${XR_PLATFORM_DEFINES}) + + unset(XR_PLATFORM_DEFINES) +endif() + add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index a3cc9aa1df5..aafe374a93b 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -30,21 +30,6 @@ extern "C" { #endif -/** - * Creates a "handle" for a C++ GHOST object. - * A handle is just an opaque pointer to an empty struct. - * In the API the pointer is cast to the actual C++ class. - * The 'name' argument to the macro is the name of the handle to create. - */ - -GHOST_DECLARE_HANDLE(GHOST_SystemHandle); -GHOST_DECLARE_HANDLE(GHOST_TimerTaskHandle); -GHOST_DECLARE_HANDLE(GHOST_WindowHandle); -GHOST_DECLARE_HANDLE(GHOST_EventHandle); -GHOST_DECLARE_HANDLE(GHOST_RectangleHandle); -GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle); -GHOST_DECLARE_HANDLE(GHOST_ContextHandle); - /** * Definition of a callback routine that receives events. * \param event The event received. @@ -1006,6 +991,91 @@ extern void GHOST_BeginIME(GHOST_WindowHandle windowhandle, */ extern void GHOST_EndIME(GHOST_WindowHandle windowhandle); +#ifdef WITH_XR_OPENXR + +/* XR-context */ + +/** + * Set a custom callback to be executed whenever an error occurs. Should be set before calling + * #GHOST_XrContextCreate() to get error handling during context creation too. + * + * \param customdata: Handle to some data that will get passed to \a handler_fn should an error be + * thrown. + */ +void GHOST_XrErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata); + +/** + * \brief Initialize the Ghost XR-context. + * + * Includes setting up the OpenXR runtime link, querying available extensions and API layers, + * enabling extensions and API layers. + * + * \param create_info: Options for creating the XR-context, e.g. debug-flags and ordered array of + * graphics bindings to try enabling. + */ +GHOST_XrContextHandle GHOST_XrContextCreate(const GHOST_XrContextCreateInfo *create_info); +/** + * Free a XR-context involving OpenXR runtime link destruction and freeing of all internal data. + */ +void GHOST_XrContextDestroy(GHOST_XrContextHandle xr_context); + +/** + * Set callbacks for binding and unbinding a graphics context for a session. The binding callback + * may create a new graphics context thereby. In fact that's the sole reason for this callback + * approach to binding. Just make sure to have an unbind function set that properly destructs. + * + * \param bind_fn: Function to retrieve (possibly create) a graphics context. + * \param unbind_fn: Function to release (possibly free) a graphics context. + */ +void GHOST_XrGraphicsContextBindFuncs(GHOST_XrContextHandle xr_context, + GHOST_XrGraphicsContextBindFn bind_fn, + GHOST_XrGraphicsContextUnbindFn unbind_fn); + +/** + * Set the drawing callback for views. A view would typically be either the left or the right eye, + * although other configurations are possible. When #GHOST_XrSessionDrawViews() is called to draw + * an XR frame, \a draw_view_fn is executed for each view. + * + * \param draw_view_fn: The callback to draw a single view for an XR frame. + */ +void GHOST_XrDrawViewFunc(GHOST_XrContextHandle xr_context, GHOST_XrDrawViewFn draw_view_fn); + +/* sessions */ +/** + * Create internal session data for \a xr_context and ask the OpenXR runtime to invoke a session. + * + * \param begin_info: Options for the session creation. + */ +void GHOST_XrSessionStart(GHOST_XrContextHandle xr_context, + const GHOST_XrSessionBeginInfo *begin_info); +/** + * Destruct internal session data for \a xr_context and ask the OpenXR runtime to stop a session. + */ +void GHOST_XrSessionEnd(GHOST_XrContextHandle xr_context); +/** + * Draw a single frame by calling the view drawing callback defined by #GHOST_XrDrawViewFunc() for + * each view and submit it to the OpenXR runtime. + * + * \param customdata: Handle to some data that will get passed to the view drawing callback. + */ +void GHOST_XrSessionDrawViews(GHOST_XrContextHandle xr_context, void *customdata); +/** + * Check if a \a xr_context has a session that, according to the OpenXR definition would be + * considered to be 'running' + * (https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#session_running). + */ +int GHOST_XrSessionIsRunning(const GHOST_XrContextHandle xr_context); + +/* events */ +/** + * Invoke handling of all OpenXR events for \a xr_context. Should be called on every main-loop + * iteration and will early-exit if \a xr_context is NULL (so caller doesn't have to check). + * + * \returns GHOST_kSuccess if any event was handled, otherwise GHOST_kFailure. + */ +GHOST_TSuccess GHOST_XrEventsHandle(GHOST_XrContextHandle xr_context); +#endif + #ifdef __cplusplus } #endif diff --git a/intern/ghost/GHOST_IXrContext.h b/intern/ghost/GHOST_IXrContext.h new file mode 100644 index 00000000000..362bc923ee8 --- /dev/null +++ b/intern/ghost/GHOST_IXrContext.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#ifndef __GHOST_IXRCONTEXT_H__ +#define __GHOST_IXRCONTEXT_H__ + +#include "GHOST_Types.h" + +class GHOST_IXrContext { + public: + virtual ~GHOST_IXrContext() = default; + + virtual void startSession(const GHOST_XrSessionBeginInfo *begin_info) = 0; + virtual void endSession() = 0; + virtual bool isSessionRunning() const = 0; + virtual void drawSessionViews(void *draw_customdata) = 0; + + virtual void dispatchErrorMessage(const class GHOST_XrException *) const = 0; + + virtual void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn, + GHOST_XrGraphicsContextUnbindFn unbind_fn) = 0; + virtual void setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) = 0; +}; + +#endif // __GHOST_IXRCONTEXT_H__ diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 8bc75d01b96..adda782b96d 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -41,6 +41,22 @@ } * name #endif +/** + * Creates a "handle" for a C++ GHOST object. + * A handle is just an opaque pointer to an empty struct. + * In the API the pointer is cast to the actual C++ class. + * The 'name' argument to the macro is the name of the handle to create. + */ + +GHOST_DECLARE_HANDLE(GHOST_SystemHandle); +GHOST_DECLARE_HANDLE(GHOST_TimerTaskHandle); +GHOST_DECLARE_HANDLE(GHOST_WindowHandle); +GHOST_DECLARE_HANDLE(GHOST_EventHandle); +GHOST_DECLARE_HANDLE(GHOST_RectangleHandle); +GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle); +GHOST_DECLARE_HANDLE(GHOST_ContextHandle); +GHOST_DECLARE_HANDLE(GHOST_XrContextHandle); + typedef char GHOST_TInt8; typedef unsigned char GHOST_TUns8; typedef short GHOST_TInt16; @@ -580,4 +596,78 @@ struct GHOST_TimerTaskHandle__; typedef void (*GHOST_TimerProcPtr)(struct GHOST_TimerTaskHandle__ *task, GHOST_TUns64 time); #endif +#ifdef WITH_XR_OPENXR + +/** + * The XR view (i.e. the OpenXR runtime) may require a different graphics library than OpenGL. An + * offscreen texture of the viewport will then be drawn into using OpenGL, but the final texture + * draw call will happen through another lib (say DirectX). + * + * This enum defines the possible graphics bindings to attempt to enable. + */ +typedef enum { + GHOST_kXrGraphicsUnknown = 0, + GHOST_kXrGraphicsOpenGL, +# ifdef WIN32 + GHOST_kXrGraphicsD3D11, +# endif + /* For later */ + // GHOST_kXrGraphicsVulkan, +} GHOST_TXrGraphicsBinding; +/* An array of GHOST_TXrGraphicsBinding items defining the candidate bindings to use. The first + * available candidate will be chosen, so order defines priority. */ +typedef const GHOST_TXrGraphicsBinding *GHOST_XrGraphicsBindingCandidates; + +typedef struct { + float position[3]; + /* Blender convention (w, x, y, z) */ + float orientation_quat[4]; +} GHOST_XrPose; + +enum { + GHOST_kXrContextDebug = (1 << 0), + GHOST_kXrContextDebugTime = (1 << 1), +}; + +typedef struct { + const GHOST_XrGraphicsBindingCandidates gpu_binding_candidates; + unsigned int gpu_binding_candidates_count; + + unsigned int context_flag; +} GHOST_XrContextCreateInfo; + +typedef struct { + GHOST_XrPose base_pose; +} GHOST_XrSessionBeginInfo; + +typedef struct { + int ofsx, ofsy; + int width, height; + + GHOST_XrPose pose; + + struct { + float angle_left, angle_right; + float angle_up, angle_down; + } fov; + + /** Set if the buffer should be submitted with a srgb transfer applied. */ + char expects_srgb_buffer; +} GHOST_XrDrawViewInfo; + +typedef struct { + const char *user_message; + + void *customdata; +} GHOST_XrError; + +typedef void (*GHOST_XrErrorHandlerFn)(const GHOST_XrError *); + +typedef void *(*GHOST_XrGraphicsContextBindFn)(GHOST_TXrGraphicsBinding graphics_lib); +typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_TXrGraphicsBinding graphics_lib, + void *graphics_context); +typedef void (*GHOST_XrDrawViewFn)(const GHOST_XrDrawViewInfo *draw_view, void *customdata); + +#endif + #endif // __GHOST_TYPES_H__ diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 9a7e3da828b..60d20474a5a 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -30,7 +30,11 @@ #include "GHOST_ISystem.h" #include "GHOST_IEvent.h" #include "GHOST_IEventConsumer.h" +#ifdef WITH_XR_OPENXR +# include "GHOST_IXrContext.h" +#endif #include "intern/GHOST_CallbackEventConsumer.h" +#include "intern/GHOST_XrException.h" GHOST_SystemHandle GHOST_CreateSystem(void) { @@ -914,3 +918,63 @@ void GHOST_EndIME(GHOST_WindowHandle windowhandle) } #endif /* WITH_INPUT_IME */ + +#ifdef WITH_XR_OPENXR + +# define GHOST_XR_CAPI_CALL(call, ctx) \ + try { \ + call; \ + } \ + catch (GHOST_XrException & e) { \ + (ctx)->dispatchErrorMessage(&e); \ + } + +# define GHOST_XR_CAPI_CALL_RET(call, ctx) \ + try { \ + return call; \ + } \ + catch (GHOST_XrException & e) { \ + (ctx)->dispatchErrorMessage(&e); \ + } + +void GHOST_XrSessionStart(GHOST_XrContextHandle xr_contexthandle, + const GHOST_XrSessionBeginInfo *begin_info) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XR_CAPI_CALL(xr_context->startSession(begin_info), xr_context); +} + +void GHOST_XrSessionEnd(GHOST_XrContextHandle xr_contexthandle) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XR_CAPI_CALL(xr_context->endSession(), xr_context); +} + +void GHOST_XrSessionDrawViews(GHOST_XrContextHandle xr_contexthandle, void *draw_customdata) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XR_CAPI_CALL(xr_context->drawSessionViews(draw_customdata), xr_context); +} + +int GHOST_XrSessionIsRunning(const GHOST_XrContextHandle xr_contexthandle) +{ + const GHOST_IXrContext *xr_context = (const GHOST_IXrContext *)xr_contexthandle; + GHOST_XR_CAPI_CALL_RET(xr_context->isSessionRunning(), xr_context); + return 0; /* Only reached if exception is thrown. */ +} + +void GHOST_XrGraphicsContextBindFuncs(GHOST_XrContextHandle xr_contexthandle, + GHOST_XrGraphicsContextBindFn bind_fn, + GHOST_XrGraphicsContextUnbindFn unbind_fn) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XR_CAPI_CALL(xr_context->setGraphicsContextBindFuncs(bind_fn, unbind_fn), xr_context); +} + +void GHOST_XrDrawViewFunc(GHOST_XrContextHandle xr_contexthandle, GHOST_XrDrawViewFn draw_view_fn) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XR_CAPI_CALL(xr_context->setDrawViewFunc(draw_view_fn), xr_context); +} + +#endif diff --git a/intern/ghost/intern/GHOST_ContextD3D.h b/intern/ghost/intern/GHOST_ContextD3D.h index 601282fc80f..c482992a6e2 100644 --- a/intern/ghost/intern/GHOST_ContextD3D.h +++ b/intern/ghost/intern/GHOST_ContextD3D.h @@ -30,6 +30,9 @@ #include "GHOST_Context.h" class GHOST_ContextD3D : public GHOST_Context { + /* XR code needs low level graphics data to send to OpenXR. */ + friend class GHOST_XrGraphicsBindingD3D; + public: GHOST_ContextD3D(bool stereoVisual, HWND hWnd); ~GHOST_ContextD3D(); diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp index fac75f299fc..ecf824aafb7 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.cpp +++ b/intern/ghost/intern/GHOST_ContextGLX.cpp @@ -273,6 +273,7 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() m_window = (Window)glXCreatePbuffer(m_display, framebuffer_config[0], pbuffer_attribs); } + m_fbconfig = framebuffer_config[0]; XFree(framebuffer_config); } } diff --git a/intern/ghost/intern/GHOST_ContextGLX.h b/intern/ghost/intern/GHOST_ContextGLX.h index ba8df7dac1b..07d2601cd17 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.h +++ b/intern/ghost/intern/GHOST_ContextGLX.h @@ -38,6 +38,9 @@ #endif class GHOST_ContextGLX : public GHOST_Context { + /* XR code needs low level graphics data to send to OpenXR. */ + friend class GHOST_XrGraphicsBindingOpenGL; + public: /** * Constructor. diff --git a/intern/ghost/intern/GHOST_ContextWGL.h b/intern/ghost/intern/GHOST_ContextWGL.h index a990e42c4ff..a8d2c18b463 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.h +++ b/intern/ghost/intern/GHOST_ContextWGL.h @@ -35,6 +35,9 @@ #endif class GHOST_ContextWGL : public GHOST_Context { + /* XR code needs low level graphics data to send to OpenXR. */ + friend class GHOST_XrGraphicsBindingOpenGL; + public: /** * Constructor. diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h new file mode 100644 index 00000000000..19fe00cdad5 --- /dev/null +++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#ifndef __GHOST_IXRGRAPHICSBINDING_H__ +#define __GHOST_IXRGRAPHICSBINDING_H__ + +#include +#include +#include + +#include "GHOST_Xr_openxr_includes.h" + +class GHOST_IXrGraphicsBinding { + friend std::unique_ptr GHOST_XrGraphicsBindingCreateFromType( + GHOST_TXrGraphicsBinding type); + + public: + union { +#if defined(WITH_X11) + XrGraphicsBindingOpenGLXlibKHR glx; +#elif defined(WIN32) + XrGraphicsBindingOpenGLWin32KHR wgl; + XrGraphicsBindingD3D11KHR d3d11; +#endif + } oxr_binding; + + /** + * Does __not__ require this object is initialized (can be called prior to + * #initFromGhostContext). It's actually meant to be called first. + * + * \param r_requirement_info Return argument to retrieve an informal string on the requirements + * to be met. Useful for error/debug messages. + */ + virtual bool checkVersionRequirements(class GHOST_Context *ghost_ctx, + XrInstance instance, + XrSystemId system_id, + std::string *r_requirement_info) const = 0; + virtual void initFromGhostContext(class GHOST_Context *ghost_ctx) = 0; + virtual bool chooseSwapchainFormat(const std::vector &runtime_formats, + int64_t *r_result) const = 0; + virtual std::vector createSwapchainImages( + uint32_t image_count) = 0; + virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image, + const GHOST_XrDrawViewInfo *draw_info) = 0; + + protected: + /* Use GHOST_XrGraphicsBindingCreateFromType! */ + GHOST_IXrGraphicsBinding() = default; +}; + +std::unique_ptr GHOST_XrGraphicsBindingCreateFromType( + GHOST_TXrGraphicsBinding type); + +#endif /* __GHOST_IXRGRAPHICSBINDING_H__ */ diff --git a/intern/ghost/intern/GHOST_Xr.cpp b/intern/ghost/intern/GHOST_Xr.cpp new file mode 100644 index 00000000000..2f122ca8e13 --- /dev/null +++ b/intern/ghost/intern/GHOST_Xr.cpp @@ -0,0 +1,59 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + * + * Abstraction for XR (VR, AR, MR, ..) access via OpenXR. + */ + +#include +#include + +#include "GHOST_C-api.h" + +#include "GHOST_Xr_intern.h" +#include "GHOST_XrContext.h" +#include "GHOST_XrException.h" + +GHOST_XrContextHandle GHOST_XrContextCreate(const GHOST_XrContextCreateInfo *create_info) +{ + GHOST_XrContext *xr_context = new GHOST_XrContext(create_info); + + /* TODO GHOST_XrContext's should probably be owned by the GHOST_System, which will handle context + * creation and destruction. Try-catch logic can be moved to C-API then. */ + try { + xr_context->initialize(create_info); + } + catch (GHOST_XrException &e) { + xr_context->dispatchErrorMessage(&e); + delete xr_context; + + return nullptr; + } + + return (GHOST_XrContextHandle)xr_context; +} + +void GHOST_XrContextDestroy(GHOST_XrContextHandle xr_contexthandle) +{ + delete (GHOST_XrContext *)xr_contexthandle; +} + +void GHOST_XrErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata) +{ + GHOST_XrContext::setErrorHandler(handler_fn, customdata); +} diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp new file mode 100644 index 00000000000..410837e9805 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -0,0 +1,550 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + * + * Abstraction for XR (VR, AR, MR, ..) access via OpenXR. + */ + +#include +#include +#include + +#include "GHOST_Types.h" +#include "GHOST_Xr_intern.h" +#include "GHOST_XrException.h" +#include "GHOST_XrSession.h" + +#include "GHOST_XrContext.h" + +struct OpenXRInstanceData { + XrInstance instance = XR_NULL_HANDLE; + XrInstanceProperties instance_properties = {}; + + std::vector extensions; + std::vector layers; + + static PFN_xrCreateDebugUtilsMessengerEXT s_xrCreateDebugUtilsMessengerEXT_fn; + static PFN_xrDestroyDebugUtilsMessengerEXT s_xrDestroyDebugUtilsMessengerEXT_fn; + + XrDebugUtilsMessengerEXT debug_messenger = XR_NULL_HANDLE; +}; + +PFN_xrCreateDebugUtilsMessengerEXT OpenXRInstanceData::s_xrCreateDebugUtilsMessengerEXT_fn = + nullptr; +PFN_xrDestroyDebugUtilsMessengerEXT OpenXRInstanceData::s_xrDestroyDebugUtilsMessengerEXT_fn = + nullptr; + +GHOST_XrErrorHandlerFn GHOST_XrContext::s_error_handler = nullptr; +void *GHOST_XrContext::s_error_handler_customdata = nullptr; + +/* -------------------------------------------------------------------- */ +/** \name Create, Initialize and Destruct + * + * \{ */ + +GHOST_XrContext::GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info) + : m_oxr(new OpenXRInstanceData()), + m_debug(create_info->context_flag & GHOST_kXrContextDebug), + m_debug_time(create_info->context_flag & GHOST_kXrContextDebugTime) +{ +} + +GHOST_XrContext::~GHOST_XrContext() +{ + /* Destroy session data first. Otherwise xrDestroyInstance will implicitly do it, before the + * session had a chance to do so explicitly. */ + m_session = nullptr; + + if (m_oxr->debug_messenger != XR_NULL_HANDLE) { + assert(m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn != nullptr); + m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn(m_oxr->debug_messenger); + } + if (m_oxr->instance != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroyInstance(m_oxr->instance)); + m_oxr->instance = XR_NULL_HANDLE; + } +} + +void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info) +{ + initApiLayers(); + initExtensions(); + if (isDebugMode()) { + printAvailableAPILayersAndExtensionsInfo(); + } + + m_gpu_binding_type = determineGraphicsBindingTypeToEnable(create_info); + + assert(m_oxr->instance == XR_NULL_HANDLE); + createOpenXRInstance(); + storeInstanceProperties(); + printInstanceInfo(); + if (isDebugMode()) { + initDebugMessenger(); + } +} + +void GHOST_XrContext::createOpenXRInstance() +{ + XrInstanceCreateInfo create_info = {XR_TYPE_INSTANCE_CREATE_INFO}; + + std::string("Blender").copy(create_info.applicationInfo.applicationName, + XR_MAX_APPLICATION_NAME_SIZE); + create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; + + getAPILayersToEnable(m_enabled_layers); + getExtensionsToEnable(m_enabled_extensions); + create_info.enabledApiLayerCount = m_enabled_layers.size(); + create_info.enabledApiLayerNames = m_enabled_layers.data(); + create_info.enabledExtensionCount = m_enabled_extensions.size(); + create_info.enabledExtensionNames = m_enabled_extensions.data(); + if (isDebugMode()) { + printExtensionsAndAPILayersToEnable(); + } + + CHECK_XR(xrCreateInstance(&create_info, &m_oxr->instance), + "Failed to connect to an OpenXR runtime."); +} + +void GHOST_XrContext::storeInstanceProperties() +{ + const std::map runtime_map = { + {"Monado(XRT) by Collabora et al", OPENXR_RUNTIME_MONADO}, + {"Oculus", OPENXR_RUNTIME_OCULUS}, + {"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}}; + decltype(runtime_map)::const_iterator runtime_map_iter; + + m_oxr->instance_properties.type = XR_TYPE_INSTANCE_PROPERTIES; + CHECK_XR(xrGetInstanceProperties(m_oxr->instance, &m_oxr->instance_properties), + "Failed to get OpenXR runtime information. Do you have an active runtime set up?"); + + runtime_map_iter = runtime_map.find(m_oxr->instance_properties.runtimeName); + if (runtime_map_iter != runtime_map.end()) { + m_runtime_id = runtime_map_iter->second; + } +} + +/** \} */ /* Create, Initialize and Destruct */ + +/* -------------------------------------------------------------------- */ +/** \name Debug Printing + * + * \{ */ + +void GHOST_XrContext::printInstanceInfo() +{ + assert(m_oxr->instance != XR_NULL_HANDLE); + + printf("Connected to OpenXR runtime: %s (Version %u.%u.%u)\n", + m_oxr->instance_properties.runtimeName, + XR_VERSION_MAJOR(m_oxr->instance_properties.runtimeVersion), + XR_VERSION_MINOR(m_oxr->instance_properties.runtimeVersion), + XR_VERSION_PATCH(m_oxr->instance_properties.runtimeVersion)); +} + +void GHOST_XrContext::printAvailableAPILayersAndExtensionsInfo() +{ + puts("Available OpenXR API-layers/extensions:"); + for (XrApiLayerProperties &layer_info : m_oxr->layers) { + printf("Layer: %s\n", layer_info.layerName); + } + for (XrExtensionProperties &ext_info : m_oxr->extensions) { + printf("Extension: %s\n", ext_info.extensionName); + } +} + +void GHOST_XrContext::printExtensionsAndAPILayersToEnable() +{ + for (const char *layer_name : m_enabled_layers) { + printf("Enabling OpenXR API-Layer: %s\n", layer_name); + } + for (const char *ext_name : m_enabled_extensions) { + printf("Enabling OpenXR Extension: %s\n", ext_name); + } +} + +static XrBool32 debug_messenger_func(XrDebugUtilsMessageSeverityFlagsEXT /*messageSeverity*/, + XrDebugUtilsMessageTypeFlagsEXT /*messageTypes*/, + const XrDebugUtilsMessengerCallbackDataEXT *callbackData, + void * /*userData*/) +{ + puts("OpenXR Debug Message:"); + puts(callbackData->message); + return XR_FALSE; /* OpenXR spec suggests always returning false. */ +} + +void GHOST_XrContext::initDebugMessenger() +{ + XrDebugUtilsMessengerCreateInfoEXT create_info = {XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + + /* Extension functions need to be obtained through xrGetInstanceProcAddr(). */ + if (XR_FAILED(xrGetInstanceProcAddr( + m_oxr->instance, + "xrCreateDebugUtilsMessengerEXT", + (PFN_xrVoidFunction *)&m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn)) || + XR_FAILED(xrGetInstanceProcAddr( + m_oxr->instance, + "xrDestroyDebugUtilsMessengerEXT", + (PFN_xrVoidFunction *)&m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn))) { + m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn = nullptr; + m_oxr->s_xrDestroyDebugUtilsMessengerEXT_fn = nullptr; + + fprintf(stderr, + "Could not use XR_EXT_debug_utils to enable debug prints. Not a fatal error, " + "continuing without the messenger.\n"); + return; + } + + create_info.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + create_info.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + create_info.userCallback = debug_messenger_func; + + if (XR_FAILED(m_oxr->s_xrCreateDebugUtilsMessengerEXT_fn( + m_oxr->instance, &create_info, &m_oxr->debug_messenger))) { + fprintf(stderr, + "Failed to create OpenXR debug messenger. Not a fatal error, continuing without the " + "messenger.\n"); + return; + } +} + +/** \} */ /* Debug Printing */ + +/* -------------------------------------------------------------------- */ +/** \name Error handling + * + * \{ */ + +void GHOST_XrContext::dispatchErrorMessage(const GHOST_XrException *exception) const +{ + GHOST_XrError error; + + error.user_message = exception->m_msg; + error.customdata = s_error_handler_customdata; + + if (isDebugMode()) { + fprintf(stderr, + "Error: \t%s\n\tOpenXR error value: %i\n", + error.user_message, + exception->m_result); + } + + /* Potentially destroys GHOST_XrContext */ + s_error_handler(&error); +} + +void GHOST_XrContext::setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata) +{ + s_error_handler = handler_fn; + s_error_handler_customdata = customdata; +} + +/** \} */ /* Error handling */ + +/* -------------------------------------------------------------------- */ +/** \name OpenXR API-Layers and Extensions + * + * \{ */ + +/** + * \param layer_name May be NULL for extensions not belonging to a specific layer. + */ +void GHOST_XrContext::initExtensionsEx(std::vector &extensions, + const char *layer_name) +{ + uint32_t extension_count = 0; + + /* Get count for array creation/init first. */ + CHECK_XR(xrEnumerateInstanceExtensionProperties(layer_name, 0, &extension_count, nullptr), + "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); + + if (extension_count == 0) { + /* Extensions are optional, can successfully exit. */ + return; + } + + for (uint32_t i = 0; i < extension_count; i++) { + XrExtensionProperties ext = {XR_TYPE_EXTENSION_PROPERTIES}; + extensions.push_back(ext); + } + + /* Actually get the extensions. */ + CHECK_XR(xrEnumerateInstanceExtensionProperties( + layer_name, extension_count, &extension_count, extensions.data()), + "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); +} + +void GHOST_XrContext::initExtensions() +{ + initExtensionsEx(m_oxr->extensions, nullptr); +} + +void GHOST_XrContext::initApiLayers() +{ + uint32_t layer_count = 0; + + /* Get count for array creation/init first. */ + CHECK_XR(xrEnumerateApiLayerProperties(0, &layer_count, nullptr), + "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); + + if (layer_count == 0) { + /* Layers are optional, can safely exit. */ + return; + } + + m_oxr->layers = std::vector(layer_count); + for (XrApiLayerProperties &layer : m_oxr->layers) { + layer.type = XR_TYPE_API_LAYER_PROPERTIES; + } + + /* Actually get the layers. */ + CHECK_XR(xrEnumerateApiLayerProperties(layer_count, &layer_count, m_oxr->layers.data()), + "Failed to query OpenXR runtime information. Do you have an active runtime set up?"); + for (XrApiLayerProperties &layer : m_oxr->layers) { + /* Each layer may have own extensions. */ + initExtensionsEx(m_oxr->extensions, layer.layerName); + } +} + +static bool openxr_layer_is_available(const std::vector layers_info, + const std::string &layer_name) +{ + for (const XrApiLayerProperties &layer_info : layers_info) { + if (layer_info.layerName == layer_name) { + return true; + } + } + + return false; +} + +static bool openxr_extension_is_available(const std::vector extensions_info, + const std::string &extension_name) +{ + for (const XrExtensionProperties &ext_info : extensions_info) { + if (ext_info.extensionName == extension_name) { + return true; + } + } + + return false; +} + +/** + * Gather an array of names for the API-layers to enable. + */ +void GHOST_XrContext::getAPILayersToEnable(std::vector &r_ext_names) +{ + static std::vector try_layers; + + try_layers.clear(); + + if (isDebugMode()) { + try_layers.push_back("XR_APILAYER_LUNARG_core_validation"); + } + + r_ext_names.reserve(try_layers.size()); + + for (const std::string &layer : try_layers) { + if (openxr_layer_is_available(m_oxr->layers, layer)) { + r_ext_names.push_back(layer.c_str()); + } + } +} + +static const char *openxr_ext_name_from_wm_gpu_binding(GHOST_TXrGraphicsBinding binding) +{ + switch (binding) { + case GHOST_kXrGraphicsOpenGL: + return XR_KHR_OPENGL_ENABLE_EXTENSION_NAME; +#ifdef WIN32 + case GHOST_kXrGraphicsD3D11: + return XR_KHR_D3D11_ENABLE_EXTENSION_NAME; +#endif + case GHOST_kXrGraphicsUnknown: + assert(!"Could not identify graphics binding to choose."); + return nullptr; + } + + return nullptr; +} + +/** + * Gather an array of names for the extensions to enable. + */ +void GHOST_XrContext::getExtensionsToEnable(std::vector &r_ext_names) +{ + assert(m_gpu_binding_type != GHOST_kXrGraphicsUnknown); + + const char *gpu_binding = openxr_ext_name_from_wm_gpu_binding(m_gpu_binding_type); + static std::vector try_ext; + + try_ext.clear(); + + /* Try enabling debug extension. */ +#ifndef WIN32 + if (isDebugMode()) { + try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME); + } +#endif + + r_ext_names.reserve(try_ext.size() + 1); /* + 1 for graphics binding extension. */ + + /* Add graphics binding extension. */ + assert(gpu_binding); + assert(openxr_extension_is_available(m_oxr->extensions, gpu_binding)); + r_ext_names.push_back(gpu_binding); + + for (const std::string &ext : try_ext) { + if (openxr_extension_is_available(m_oxr->extensions, ext)) { + r_ext_names.push_back(ext.c_str()); + } + } +} + +/** + * Decide which graphics binding extension to use based on + * #GHOST_XrContextCreateInfo.gpu_binding_candidates and available extensions. + */ +GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToEnable( + const GHOST_XrContextCreateInfo *create_info) +{ + assert(create_info->gpu_binding_candidates != NULL); + assert(create_info->gpu_binding_candidates_count > 0); + + for (uint32_t i = 0; i < create_info->gpu_binding_candidates_count; i++) { + assert(create_info->gpu_binding_candidates[i] != GHOST_kXrGraphicsUnknown); + const char *ext_name = openxr_ext_name_from_wm_gpu_binding( + create_info->gpu_binding_candidates[i]); + if (openxr_extension_is_available(m_oxr->extensions, ext_name)) { + return create_info->gpu_binding_candidates[i]; + } + } + + return GHOST_kXrGraphicsUnknown; +} + +/** \} */ /* OpenXR API-Layers and Extensions */ + +/* -------------------------------------------------------------------- */ +/** \name Session management + * + * Manage session lifetime and delegate public calls to #GHOST_XrSession. + * \{ */ + +void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info) +{ + if (m_session == nullptr) { + m_session = std::unique_ptr(new GHOST_XrSession(this)); + } + + m_session->start(begin_info); +} + +void GHOST_XrContext::endSession() +{ + m_session->requestEnd(); +} + +bool GHOST_XrContext::isSessionRunning() const +{ + return m_session && m_session->isRunning(); +} + +void GHOST_XrContext::drawSessionViews(void *draw_customdata) +{ + m_session->draw(draw_customdata); +} + +/** + * Delegates event to session, allowing context to destruct the session if needed. + */ +void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle) +{ + if (m_session && + m_session->handleStateChangeEvent(lifecycle) == GHOST_XrSession::SESSION_DESTROY) { + m_session = nullptr; + } +} + +/** \} */ /* Session Management */ + +/* -------------------------------------------------------------------- */ +/** \name Public Accessors and Mutators + * + * Public as in, exposed in the Ghost API. + * \{ */ + +void GHOST_XrContext::setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn, + GHOST_XrGraphicsContextUnbindFn unbind_fn) +{ + if (m_session) { + m_session->unbindGraphicsContext(); + } + m_custom_funcs.gpu_ctx_bind_fn = bind_fn; + m_custom_funcs.gpu_ctx_unbind_fn = unbind_fn; +} + +void GHOST_XrContext::setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) +{ + m_custom_funcs.draw_view_fn = draw_view_fn; +} + +/** \} */ /* Public Accessors and Mutators */ + +/* -------------------------------------------------------------------- */ +/** \name Ghost Internal Accessors and Mutators + * + * \{ */ + +GHOST_TXrOpenXRRuntimeID GHOST_XrContext::getOpenXRRuntimeID() const +{ + return m_runtime_id; +} + +const GHOST_XrCustomFuncs &GHOST_XrContext::getCustomFuncs() const +{ + return m_custom_funcs; +} + +GHOST_TXrGraphicsBinding GHOST_XrContext::getGraphicsBindingType() const +{ + return m_gpu_binding_type; +} + +XrInstance GHOST_XrContext::getInstance() const +{ + return m_oxr->instance; +} + +bool GHOST_XrContext::isDebugMode() const +{ + return m_debug; +} + +bool GHOST_XrContext::isDebugTimeMode() const +{ + return m_debug_time; +} + +/** \} */ /* Ghost Internal Accessors and Mutators */ diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h new file mode 100644 index 00000000000..b361fb5caf8 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrContext.h @@ -0,0 +1,127 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#ifndef __GHOST_XRCONTEXT_H__ +#define __GHOST_XRCONTEXT_H__ + +#include +#include +#include "GHOST_IXrContext.h" + +struct OpenXRInstanceData; + +struct GHOST_XrCustomFuncs { + /** Function to retrieve (possibly create) a graphics context. */ + GHOST_XrGraphicsContextBindFn gpu_ctx_bind_fn = nullptr; + /** Function to release (possibly free) a graphics context. */ + GHOST_XrGraphicsContextUnbindFn gpu_ctx_unbind_fn = nullptr; + + /** Custom per-view draw function for Blender side drawing. */ + GHOST_XrDrawViewFn draw_view_fn = nullptr; +}; + +/** + * In some occasions, runtime specific handling is needed, e.g. to work around runtime bugs. + */ +enum GHOST_TXrOpenXRRuntimeID { + OPENXR_RUNTIME_MONADO, + OPENXR_RUNTIME_OCULUS, + OPENXR_RUNTIME_WMR, /* Windows Mixed Reality */ + + OPENXR_RUNTIME_UNKNOWN +}; + +/** + * \brief Main GHOST container to manage OpenXR through. + * + * Creating a context using #GHOST_XrContextCreate involves dynamically connecting to the OpenXR + * runtime, likely reading the OS OpenXR configuration (i.e. active_runtime.json). So this is + * something that should better be done using lazy-initialization. + */ +class GHOST_XrContext : public GHOST_IXrContext { + public: + GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info); + ~GHOST_XrContext(); + void initialize(const GHOST_XrContextCreateInfo *create_info); + + void startSession(const GHOST_XrSessionBeginInfo *begin_info) override; + void endSession() override; + bool isSessionRunning() const override; + void drawSessionViews(void *draw_customdata) override; + + static void setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata); + void dispatchErrorMessage(const class GHOST_XrException *exception) const override; + + void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn, + GHOST_XrGraphicsContextUnbindFn unbind_fn) override; + void setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) override; + + void handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle); + + GHOST_TXrOpenXRRuntimeID getOpenXRRuntimeID() const; + const GHOST_XrCustomFuncs &getCustomFuncs() const; + GHOST_TXrGraphicsBinding getGraphicsBindingType() const; + XrInstance getInstance() const; + bool isDebugMode() const; + bool isDebugTimeMode() const; + + private: + static GHOST_XrErrorHandlerFn s_error_handler; + static void *s_error_handler_customdata; + + std::unique_ptr m_oxr; + + GHOST_TXrOpenXRRuntimeID m_runtime_id = OPENXR_RUNTIME_UNKNOWN; + + /* The active GHOST XR Session. Null while no session runs. */ + std::unique_ptr m_session; + + /** Active graphics binding type. */ + GHOST_TXrGraphicsBinding m_gpu_binding_type = GHOST_kXrGraphicsUnknown; + + /** Names of enabled extensions. */ + std::vector m_enabled_extensions; + /** Names of enabled API-layers. */ + std::vector m_enabled_layers; + + GHOST_XrCustomFuncs m_custom_funcs; + + /** Enable debug message prints and OpenXR API validation layers. */ + bool m_debug = false; + bool m_debug_time = false; + + void createOpenXRInstance(); + void storeInstanceProperties(); + void initDebugMessenger(); + + void printInstanceInfo(); + void printAvailableAPILayersAndExtensionsInfo(); + void printExtensionsAndAPILayersToEnable(); + + void initApiLayers(); + void initExtensions(); + void initExtensionsEx(std::vector &extensions, const char *layer_name); + void getAPILayersToEnable(std::vector &r_ext_names); + void getExtensionsToEnable(std::vector &r_ext_names); + GHOST_TXrGraphicsBinding determineGraphicsBindingTypeToEnable( + const GHOST_XrContextCreateInfo *create_info); +}; + +#endif // __GHOST_XRCONTEXT_H__ diff --git a/intern/ghost/intern/GHOST_XrEvent.cpp b/intern/ghost/intern/GHOST_XrEvent.cpp new file mode 100644 index 00000000000..dfee2e95f10 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrEvent.cpp @@ -0,0 +1,64 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#include + +#include "GHOST_C-api.h" +#include "GHOST_Xr_intern.h" +#include "GHOST_XrContext.h" + +static bool GHOST_XrEventPollNext(XrInstance instance, XrEventDataBuffer &r_event_data) +{ + /* (Re-)initialize as required by specification. */ + r_event_data.type = XR_TYPE_EVENT_DATA_BUFFER; + r_event_data.next = nullptr; + + return (xrPollEvent(instance, &r_event_data) == XR_SUCCESS); +} + +GHOST_TSuccess GHOST_XrEventsHandle(GHOST_XrContextHandle xr_contexthandle) +{ + GHOST_XrContext *xr_context = (GHOST_XrContext *)xr_contexthandle; + XrEventDataBuffer event_buffer; /* Structure big enough to hold all possible events. */ + + if (xr_context == NULL) { + return GHOST_kFailure; + } + + while (GHOST_XrEventPollNext(xr_context->getInstance(), event_buffer)) { + XrEventDataBaseHeader *event = (XrEventDataBaseHeader *)&event_buffer; + + switch (event->type) { + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: + xr_context->handleSessionStateChange((XrEventDataSessionStateChanged *)event); + return GHOST_kSuccess; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + GHOST_XrContextDestroy(xr_contexthandle); + return GHOST_kSuccess; + default: + if (xr_context->isDebugMode()) { + printf("Unhandled event: %i\n", event->type); + } + return GHOST_kFailure; + } + } + + return GHOST_kFailure; +} diff --git a/intern/ghost/intern/GHOST_XrException.h b/intern/ghost/intern/GHOST_XrException.h new file mode 100644 index 00000000000..9f779961e4f --- /dev/null +++ b/intern/ghost/intern/GHOST_XrException.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#ifndef __GHOST_XREXCEPTION_H__ +#define __GHOST_XREXCEPTION_H__ + +#include + +class GHOST_XrException : public std::exception { + friend class GHOST_XrContext; + + public: + GHOST_XrException(const char *msg, int result = 0) + : std::exception(), m_msg(msg), m_result(result) + { + } + + const char *what() const noexcept override + { + return m_msg; + } + + private: + const char *m_msg; + int m_result; +}; + +#endif // __GHOST_XREXCEPTION_H__ diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp new file mode 100644 index 00000000000..ddc757b8f8a --- /dev/null +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -0,0 +1,316 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#include +#include +#include + +#if defined(WITH_X11) +# include "GHOST_ContextGLX.h" +#elif defined(WIN32) +# include "GHOST_ContextWGL.h" +# include "GHOST_ContextD3D.h" +#endif +#include "GHOST_C-api.h" +#include "GHOST_Xr_intern.h" + +#include "GHOST_IXrGraphicsBinding.h" + +static bool choose_swapchain_format_from_candidates(std::vector gpu_binding_formats, + std::vector runtime_formats, + int64_t *r_result) +{ + if (gpu_binding_formats.empty()) { + return false; + } + + auto res = std::find_first_of(gpu_binding_formats.begin(), + gpu_binding_formats.end(), + runtime_formats.begin(), + runtime_formats.end()); + if (res == gpu_binding_formats.end()) { + return false; + } + + *r_result = *res; + return true; +} + +class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { + public: + ~GHOST_XrGraphicsBindingOpenGL() + { + if (m_fbo != 0) { + glDeleteFramebuffers(1, &m_fbo); + } + } + + bool checkVersionRequirements(GHOST_Context *ghost_ctx, + XrInstance instance, + XrSystemId system_id, + std::string *r_requirement_info) const override + { +#if defined(WITH_X11) + GHOST_ContextGLX *ctx_gl = static_cast(ghost_ctx); +#else + GHOST_ContextWGL *ctx_gl = static_cast(ghost_ctx); +#endif + static PFN_xrGetOpenGLGraphicsRequirementsKHR s_xrGetOpenGLGraphicsRequirementsKHR_fn = + nullptr; + XrGraphicsRequirementsOpenGLKHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; + const XrVersion gl_version = XR_MAKE_VERSION( + ctx_gl->m_contextMajorVersion, ctx_gl->m_contextMinorVersion, 0); + + if (!s_xrGetOpenGLGraphicsRequirementsKHR_fn && + XR_FAILED(xrGetInstanceProcAddr( + instance, + "xrGetOpenGLGraphicsRequirementsKHR", + (PFN_xrVoidFunction *)&s_xrGetOpenGLGraphicsRequirementsKHR_fn))) { + s_xrGetOpenGLGraphicsRequirementsKHR_fn = nullptr; + } + + s_xrGetOpenGLGraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements); + + if (r_requirement_info) { + std::ostringstream strstream; + strstream << "Min OpenGL version " + << XR_VERSION_MAJOR(gpu_requirements.minApiVersionSupported) << "." + << XR_VERSION_MINOR(gpu_requirements.minApiVersionSupported) << std::endl; + strstream << "Max OpenGL version " + << XR_VERSION_MAJOR(gpu_requirements.maxApiVersionSupported) << "." + << XR_VERSION_MINOR(gpu_requirements.maxApiVersionSupported) << std::endl; + + *r_requirement_info = strstream.str(); + } + + return (gl_version >= gpu_requirements.minApiVersionSupported) && + (gl_version <= gpu_requirements.maxApiVersionSupported); + } + + void initFromGhostContext(GHOST_Context *ghost_ctx) override + { +#if defined(WITH_X11) + GHOST_ContextGLX *ctx_glx = static_cast(ghost_ctx); + XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig); + + oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; + oxr_binding.glx.xDisplay = ctx_glx->m_display; + oxr_binding.glx.glxFBConfig = ctx_glx->m_fbconfig; + oxr_binding.glx.glxDrawable = ctx_glx->m_window; + oxr_binding.glx.glxContext = ctx_glx->m_context; + oxr_binding.glx.visualid = visual_info->visualid; +#elif defined(WIN32) + GHOST_ContextWGL *ctx_wgl = static_cast(ghost_ctx); + + oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; + oxr_binding.wgl.hDC = ctx_wgl->m_hDC; + oxr_binding.wgl.hGLRC = ctx_wgl->m_hGLRC; +#endif + + /* Generate a framebuffer to use for blitting into the texture. */ + glGenFramebuffers(1, &m_fbo); + } + + bool chooseSwapchainFormat(const std::vector &runtime_formats, + int64_t *r_result) const override + { + std::vector gpu_binding_formats = {GL_RGBA8}; + return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result); + } + + std::vector createSwapchainImages(uint32_t image_count) override + { + std::vector ogl_images(image_count); + std::vector base_images; + + /* Need to return vector of base header pointers, so of a different type. Need to build a new + * list with this type, and keep the initial one alive. */ + for (XrSwapchainImageOpenGLKHR &image : ogl_images) { + image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR; + base_images.push_back(reinterpret_cast(&image)); + } + + /* Keep alive. */ + m_image_cache.push_back(std::move(ogl_images)); + + return base_images; + } + + void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image, + const GHOST_XrDrawViewInfo *draw_info) override + { + XrSwapchainImageOpenGLKHR *ogl_swapchain_image = reinterpret_cast( + swapchain_image); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo); + + glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ogl_swapchain_image->image, 0); + + glBlitFramebuffer(draw_info->ofsx, + draw_info->ofsy, + draw_info->ofsx + draw_info->width, + draw_info->ofsy + draw_info->height, + draw_info->ofsx, + draw_info->ofsy, + draw_info->ofsx + draw_info->width, + draw_info->ofsy + draw_info->height, + GL_COLOR_BUFFER_BIT, + GL_LINEAR); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + private: + std::list> m_image_cache; + GLuint m_fbo = 0; +}; + +#ifdef WIN32 +class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { + public: + ~GHOST_XrGraphicsBindingD3D() + { + if (m_shared_resource) { + m_ghost_ctx->disposeSharedOpenGLResource(m_shared_resource); + } + } + + bool checkVersionRequirements(GHOST_Context *ghost_ctx, + XrInstance instance, + XrSystemId system_id, + std::string *r_requirement_info) const override + { + GHOST_ContextD3D *ctx_dx = static_cast(ghost_ctx); + static PFN_xrGetD3D11GraphicsRequirementsKHR s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr; + XrGraphicsRequirementsD3D11KHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR}; + + if (!s_xrGetD3D11GraphicsRequirementsKHR_fn && + XR_FAILED(xrGetInstanceProcAddr( + instance, + "xrGetD3D11GraphicsRequirementsKHR", + (PFN_xrVoidFunction *)&s_xrGetD3D11GraphicsRequirementsKHR_fn))) { + s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr; + } + + s_xrGetD3D11GraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements); + + if (r_requirement_info) { + std::ostringstream strstream; + strstream << "Minimum DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel + << std::endl; + + *r_requirement_info = std::move(strstream.str()); + } + + return ctx_dx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel; + } + + void initFromGhostContext(GHOST_Context *ghost_ctx) override + { + GHOST_ContextD3D *ctx_d3d = static_cast(ghost_ctx); + + oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR; + oxr_binding.d3d11.device = ctx_d3d->m_device; + m_ghost_ctx = ctx_d3d; + } + + bool chooseSwapchainFormat(const std::vector &runtime_formats, + int64_t *r_result) const override + { + std::vector gpu_binding_formats = {DXGI_FORMAT_R8G8B8A8_UNORM}; + return choose_swapchain_format_from_candidates(gpu_binding_formats, runtime_formats, r_result); + } + + std::vector createSwapchainImages(uint32_t image_count) override + { + std::vector d3d_images(image_count); + std::vector base_images; + + /* Need to return vector of base header pointers, so of a different type. Need to build a new + * list with this type, and keep the initial one alive. */ + for (XrSwapchainImageD3D11KHR &image : d3d_images) { + image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR; + base_images.push_back(reinterpret_cast(&image)); + } + + /* Keep alive. */ + m_image_cache.push_back(std::move(d3d_images)); + + return base_images; + } + + void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image, + const GHOST_XrDrawViewInfo *draw_info) override + { + XrSwapchainImageD3D11KHR *d3d_swapchain_image = reinterpret_cast( + swapchain_image); + +# if 0 + /* Ideally we'd just create a render target view for the OpenXR swapchain image texture and + * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with + * this though. At least not with Optimus hardware. See: + * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807. + */ + + ID3D11RenderTargetView *rtv; + CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, + DXGI_FORMAT_R8G8B8A8_UNORM); + + m_ghost_ctx->m_device->CreateRenderTargetView(d3d_swapchain_image->texture, &rtv_desc, &rtv); + if (!m_shared_resource) { + m_shared_resource = m_ghost_ctx->createSharedOpenGLResource( + draw_info->width, draw_info->height, rtv); + } + m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); +# else + if (!m_shared_resource) { + m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(draw_info->width, + draw_info->height); + } + m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); + + m_ghost_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr); + m_ghost_ctx->m_device_ctx->CopyResource(d3d_swapchain_image->texture, + m_ghost_ctx->getSharedTexture2D(m_shared_resource)); +# endif + } + + private: + GHOST_ContextD3D *m_ghost_ctx; + GHOST_SharedOpenGLResource *m_shared_resource; + std::list> m_image_cache; +}; +#endif // WIN32 + +std::unique_ptr GHOST_XrGraphicsBindingCreateFromType( + GHOST_TXrGraphicsBinding type) +{ + switch (type) { + case GHOST_kXrGraphicsOpenGL: + return std::unique_ptr(new GHOST_XrGraphicsBindingOpenGL()); +#ifdef WIN32 + case GHOST_kXrGraphicsD3D11: + return std::unique_ptr(new GHOST_XrGraphicsBindingD3D()); +#endif + default: + return nullptr; + } +} diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp new file mode 100644 index 00000000000..1e2b8c0bc9d --- /dev/null +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -0,0 +1,487 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#include +#include +#include +#include +#include +#include + +#include "GHOST_C-api.h" + +#include "GHOST_IXrGraphicsBinding.h" +#include "GHOST_Xr_intern.h" +#include "GHOST_XrContext.h" +#include "GHOST_XrException.h" +#include "GHOST_XrSwapchain.h" + +#include "GHOST_XrSession.h" + +struct OpenXRSessionData { + XrSystemId system_id = XR_NULL_SYSTEM_ID; + XrSession session = XR_NULL_HANDLE; + XrSessionState session_state = XR_SESSION_STATE_UNKNOWN; + + /* Only stereo rendering supported now. */ + const XrViewConfigurationType view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + XrSpace reference_space; + std::vector views; + std::vector> swapchains; +}; + +struct GHOST_XrDrawInfo { + XrFrameState frame_state; + + /** Time at frame start to benchmark frame render durations. */ + std::chrono::high_resolution_clock::time_point frame_begin_time; + /* Time previous frames took for rendering (in ms). */ + std::list last_frame_times; +}; + +/* -------------------------------------------------------------------- */ +/** \name Create, Initialize and Destruct + * + * \{ */ + +GHOST_XrSession::GHOST_XrSession(GHOST_XrContext *xr_context) + : m_context(xr_context), m_oxr(new OpenXRSessionData()) +{ +} + +GHOST_XrSession::~GHOST_XrSession() +{ + unbindGraphicsContext(); + + m_oxr->swapchains.clear(); + + if (m_oxr->reference_space != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space)); + } + if (m_oxr->session != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroySession(m_oxr->session)); + } + + m_oxr->session = XR_NULL_HANDLE; + m_oxr->session_state = XR_SESSION_STATE_UNKNOWN; +} + +/** + * A system in OpenXR the combination of some sort of HMD plus controllers and whatever other + * devices are managed through OpenXR. So this attempts to init the HMD and the other devices. + */ +void GHOST_XrSession::initSystem() +{ + assert(m_context->getInstance() != XR_NULL_HANDLE); + assert(m_oxr->system_id == XR_NULL_SYSTEM_ID); + + XrSystemGetInfo system_info = {}; + system_info.type = XR_TYPE_SYSTEM_GET_INFO; + system_info.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + CHECK_XR(xrGetSystem(m_context->getInstance(), &system_info, &m_oxr->system_id), + "Failed to get device information. Is a device plugged in?"); +} + +/** \} */ /* Create, Initialize and Destruct */ + +/* -------------------------------------------------------------------- */ +/** \name State Management + * + * \{ */ + +static void create_reference_space(OpenXRSessionData *oxr, const GHOST_XrPose *base_pose) +{ + XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + create_info.poseInReferenceSpace.orientation.w = 1.0f; + + create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; +#if 0 +/* TODO + * + * Proper reference space set up is not supported yet. We simply hand OpenXR + * the global space as reference space and apply its pose onto the active + * camera matrix to get a basic viewing experience going. If there's no active + * camera with stick to the world origin. + * + * Once we have proper reference space set up (i.e. a way to define origin, up- + * direction and an initial view rotation perpendicular to the up-direction), + * we can hand OpenXR a proper reference pose/space. + */ + create_info.poseInReferenceSpace.position.x = base_pose->position[0]; + create_info.poseInReferenceSpace.position.y = base_pose->position[1]; + create_info.poseInReferenceSpace.position.z = base_pose->position[2]; + create_info.poseInReferenceSpace.orientation.x = base_pose->orientation_quat[1]; + create_info.poseInReferenceSpace.orientation.y = base_pose->orientation_quat[2]; + create_info.poseInReferenceSpace.orientation.z = base_pose->orientation_quat[3]; + create_info.poseInReferenceSpace.orientation.w = base_pose->orientation_quat[0]; +#else + (void)base_pose; +#endif + + CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->reference_space), + "Failed to create reference space."); +} + +void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info) +{ + assert(m_context->getInstance() != XR_NULL_HANDLE); + assert(m_oxr->session == XR_NULL_HANDLE); + if (m_context->getCustomFuncs().gpu_ctx_bind_fn == nullptr) { + throw GHOST_XrException( + "Invalid API usage: No way to bind graphics context to the XR session. Call " + "GHOST_XrGraphicsContextBindFuncs() with valid parameters before starting the " + "session (through GHOST_XrSessionStart())."); + } + + initSystem(); + + bindGraphicsContext(); + if (m_gpu_ctx == nullptr) { + throw GHOST_XrException( + "Invalid API usage: No graphics context returned through the callback set with " + "GHOST_XrGraphicsContextBindFuncs(). This is required for session starting (through " + "GHOST_XrSessionStart())."); + } + + std::string requirement_str; + m_gpu_binding = GHOST_XrGraphicsBindingCreateFromType(m_context->getGraphicsBindingType()); + if (!m_gpu_binding->checkVersionRequirements( + m_gpu_ctx, m_context->getInstance(), m_oxr->system_id, &requirement_str)) { + std::ostringstream strstream; + strstream << "Available graphics context version does not meet the following requirements: " + << requirement_str; + throw GHOST_XrException(strstream.str().c_str()); + } + m_gpu_binding->initFromGhostContext(m_gpu_ctx); + + XrSessionCreateInfo create_info = {}; + create_info.type = XR_TYPE_SESSION_CREATE_INFO; + create_info.systemId = m_oxr->system_id; + create_info.next = &m_gpu_binding->oxr_binding; + + CHECK_XR(xrCreateSession(m_context->getInstance(), &create_info, &m_oxr->session), + "Failed to create VR session. The OpenXR runtime may have additional requirements for " + "the graphics driver that are not met. Other causes are possible too however.\nTip: " + "The --debug-xr command line option for Blender might allow the runtime to output " + "detailed error information to the command line."); + + prepareDrawing(); + create_reference_space(m_oxr.get(), &begin_info->base_pose); +} + +void GHOST_XrSession::requestEnd() +{ + xrRequestExitSession(m_oxr->session); +} + +void GHOST_XrSession::end() +{ + assert(m_oxr->session != XR_NULL_HANDLE); + + CHECK_XR(xrEndSession(m_oxr->session), "Failed to cleanly end the VR session."); + unbindGraphicsContext(); + m_draw_info = nullptr; +} + +GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent( + const XrEventDataSessionStateChanged *lifecycle) +{ + m_oxr->session_state = lifecycle->state; + + /* Runtime may send events for apparently destroyed session. Our handle should be NULL then. */ + assert((m_oxr->session == XR_NULL_HANDLE) || (m_oxr->session == lifecycle->session)); + + switch (lifecycle->state) { + case XR_SESSION_STATE_READY: { + XrSessionBeginInfo begin_info = {XR_TYPE_SESSION_BEGIN_INFO}; + + begin_info.primaryViewConfigurationType = m_oxr->view_type; + CHECK_XR(xrBeginSession(m_oxr->session, &begin_info), + "Failed to cleanly begin the VR session."); + break; + } + case XR_SESSION_STATE_STOPPING: + /* Runtime will change state to STATE_EXITING, don't destruct session yet. */ + end(); + break; + case XR_SESSION_STATE_EXITING: + case XR_SESSION_STATE_LOSS_PENDING: + return SESSION_DESTROY; + default: + break; + } + + return SESSION_KEEP_ALIVE; +} +/** \} */ /* State Management */ + +/* -------------------------------------------------------------------- */ +/** \name Drawing + * + * \{ */ + +void GHOST_XrSession::prepareDrawing() +{ + std::vector view_configs; + uint32_t view_count; + + CHECK_XR( + xrEnumerateViewConfigurationViews( + m_context->getInstance(), m_oxr->system_id, m_oxr->view_type, 0, &view_count, nullptr), + "Failed to get count of view configurations."); + view_configs.resize(view_count, {XR_TYPE_VIEW_CONFIGURATION_VIEW}); + CHECK_XR(xrEnumerateViewConfigurationViews(m_context->getInstance(), + m_oxr->system_id, + m_oxr->view_type, + view_configs.size(), + &view_count, + view_configs.data()), + "Failed to get count of view configurations."); + + for (const XrViewConfigurationView &view_config : view_configs) { + m_oxr->swapchains.push_back(std::unique_ptr( + new GHOST_XrSwapchain(*m_gpu_binding, m_oxr->session, view_config))); + } + + m_oxr->views.resize(view_count, {XR_TYPE_VIEW}); + + m_draw_info = std::unique_ptr(new GHOST_XrDrawInfo()); +} + +void GHOST_XrSession::beginFrameDrawing() +{ + XrFrameWaitInfo wait_info = {XR_TYPE_FRAME_WAIT_INFO}; + XrFrameBeginInfo begin_info = {XR_TYPE_FRAME_BEGIN_INFO}; + XrFrameState frame_state = {XR_TYPE_FRAME_STATE}; + + /* TODO Blocking call. Drawing should run on a separate thread to avoid interferences. */ + CHECK_XR(xrWaitFrame(m_oxr->session, &wait_info, &frame_state), + "Failed to synchronize frame rates between Blender and the device."); + + CHECK_XR(xrBeginFrame(m_oxr->session, &begin_info), + "Failed to submit frame rendering start state."); + + m_draw_info->frame_state = frame_state; + + if (m_context->isDebugTimeMode()) { + m_draw_info->frame_begin_time = std::chrono::high_resolution_clock::now(); + } +} + +static void print_debug_timings(GHOST_XrDrawInfo *draw_info) +{ + /** Render time of last 8 frames (in ms) to calculate an average. */ + std::chrono::duration duration = std::chrono::high_resolution_clock::now() - + draw_info->frame_begin_time; + const double duration_ms = duration.count(); + const int avg_frame_count = 8; + double avg_ms_tot = 0.0; + + if (draw_info->last_frame_times.size() >= avg_frame_count) { + draw_info->last_frame_times.pop_front(); + assert(draw_info->last_frame_times.size() == avg_frame_count - 1); + } + draw_info->last_frame_times.push_back(duration_ms); + for (double ms_iter : draw_info->last_frame_times) { + avg_ms_tot += ms_iter; + } + + printf("VR frame render time: %.0fms - %.2f FPS (%.2f FPS 8 frames average)\n", + duration_ms, + 1000.0 / duration_ms, + 1000.0 / (avg_ms_tot / draw_info->last_frame_times.size())); +} + +void GHOST_XrSession::endFrameDrawing(std::vector *layers) +{ + XrFrameEndInfo end_info = {XR_TYPE_FRAME_END_INFO}; + + end_info.displayTime = m_draw_info->frame_state.predictedDisplayTime; + end_info.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + end_info.layerCount = layers->size(); + end_info.layers = layers->data(); + + CHECK_XR(xrEndFrame(m_oxr->session, &end_info), "Failed to submit rendered frame."); + + if (m_context->isDebugTimeMode()) { + print_debug_timings(m_draw_info.get()); + } +} + +void GHOST_XrSession::draw(void *draw_customdata) +{ + std::vector + projection_layer_views; /* Keep alive until xrEndFrame() call! */ + XrCompositionLayerProjection proj_layer; + std::vector layers; + + beginFrameDrawing(); + + if (m_draw_info->frame_state.shouldRender) { + proj_layer = drawLayer(projection_layer_views, draw_customdata); + layers.push_back(reinterpret_cast(&proj_layer)); + } + + endFrameDrawing(&layers); +} + +static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawViewInfo &r_info) +{ + /* Set and convert to Blender coodinate space. */ + r_info.pose.position[0] = view.pose.position.x; + r_info.pose.position[1] = view.pose.position.y; + r_info.pose.position[2] = view.pose.position.z; + r_info.pose.orientation_quat[0] = view.pose.orientation.w; + r_info.pose.orientation_quat[1] = view.pose.orientation.x; + r_info.pose.orientation_quat[2] = view.pose.orientation.y; + r_info.pose.orientation_quat[3] = view.pose.orientation.z; + + r_info.fov.angle_left = view.fov.angleLeft; + r_info.fov.angle_right = view.fov.angleRight; + r_info.fov.angle_up = view.fov.angleUp; + r_info.fov.angle_down = view.fov.angleDown; +} + +static bool ghost_xr_draw_view_expects_srgb_buffer(const GHOST_XrContext *context) +{ + /* Monado seems to be faulty and doesn't do OETF transform correctly. So expect a SRGB buffer to + * compensate. You get way too dark rendering without this, it's pretty obvious (even in the + * default startup scene). */ + return (context->getOpenXRRuntimeID() == OPENXR_RUNTIME_MONADO); +} + +void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain, + XrCompositionLayerProjectionView &r_proj_layer_view, + XrView &view, + void *draw_customdata) +{ + XrSwapchainImageBaseHeader *swapchain_image = swapchain.acquireDrawableSwapchainImage(); + GHOST_XrDrawViewInfo draw_view_info = {}; + + r_proj_layer_view.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + r_proj_layer_view.pose = view.pose; + r_proj_layer_view.fov = view.fov; + swapchain.updateCompositionLayerProjectViewSubImage(r_proj_layer_view.subImage); + + draw_view_info.expects_srgb_buffer = ghost_xr_draw_view_expects_srgb_buffer(m_context); + draw_view_info.ofsx = r_proj_layer_view.subImage.imageRect.offset.x; + draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y; + draw_view_info.width = r_proj_layer_view.subImage.imageRect.extent.width; + draw_view_info.height = r_proj_layer_view.subImage.imageRect.extent.height; + ghost_xr_draw_view_info_from_view(view, draw_view_info); + + /* Draw! */ + m_context->getCustomFuncs().draw_view_fn(&draw_view_info, draw_customdata); + m_gpu_binding->submitToSwapchainImage(swapchain_image, &draw_view_info); + + swapchain.releaseImage(); +} + +XrCompositionLayerProjection GHOST_XrSession::drawLayer( + std::vector &r_proj_layer_views, void *draw_customdata) +{ + XrViewLocateInfo viewloc_info = {XR_TYPE_VIEW_LOCATE_INFO}; + XrViewState view_state = {XR_TYPE_VIEW_STATE}; + XrCompositionLayerProjection layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + uint32_t view_count; + + viewloc_info.viewConfigurationType = m_oxr->view_type; + viewloc_info.displayTime = m_draw_info->frame_state.predictedDisplayTime; + viewloc_info.space = m_oxr->reference_space; + + CHECK_XR(xrLocateViews(m_oxr->session, + &viewloc_info, + &view_state, + m_oxr->views.size(), + &view_count, + m_oxr->views.data()), + "Failed to query frame view and projection state."); + assert(m_oxr->swapchains.size() == view_count); + + r_proj_layer_views.resize(view_count); + + for (uint32_t view_idx = 0; view_idx < view_count; view_idx++) { + drawView(*m_oxr->swapchains[view_idx], + r_proj_layer_views[view_idx], + m_oxr->views[view_idx], + draw_customdata); + } + + layer.space = m_oxr->reference_space; + layer.viewCount = r_proj_layer_views.size(); + layer.views = r_proj_layer_views.data(); + + return layer; +} + +/** \} */ /* Drawing */ + +/* -------------------------------------------------------------------- */ +/** \name State Queries + * + * \{ */ + +bool GHOST_XrSession::isRunning() const +{ + if (m_oxr->session == XR_NULL_HANDLE) { + return false; + } + switch (m_oxr->session_state) { + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_SYNCHRONIZED: + case XR_SESSION_STATE_VISIBLE: + case XR_SESSION_STATE_FOCUSED: + return true; + default: + return false; + } +} + +/** \} */ /* State Queries */ + +/* -------------------------------------------------------------------- */ +/** \name Graphics Context Injection + * + * Sessions need access to Ghost graphics context information. Additionally, this API allows + * creating contexts on the fly (created on start, destructed on end). For this, callbacks to bind + * (potentially create) and unbind (potentially destruct) a Ghost graphics context have to be set, + * which will be called on session start and end respectively. + * + * \{ */ + +void GHOST_XrSession::bindGraphicsContext() +{ + const GHOST_XrCustomFuncs &custom_funcs = m_context->getCustomFuncs(); + assert(custom_funcs.gpu_ctx_bind_fn); + m_gpu_ctx = static_cast( + custom_funcs.gpu_ctx_bind_fn(m_context->getGraphicsBindingType())); +} + +void GHOST_XrSession::unbindGraphicsContext() +{ + const GHOST_XrCustomFuncs &custom_funcs = m_context->getCustomFuncs(); + if (custom_funcs.gpu_ctx_unbind_fn) { + custom_funcs.gpu_ctx_unbind_fn(m_context->getGraphicsBindingType(), m_gpu_ctx); + } + m_gpu_ctx = nullptr; +} + +/** \} */ /* Graphics Context Injection */ diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h new file mode 100644 index 00000000000..3340385c1b6 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#ifndef __GHOST_XRSESSION_H__ +#define __GHOST_XRSESSION_H__ + +#include +#include + +class GHOST_XrContext; +struct OpenXRSessionData; +struct GHOST_XrDrawInfo; +struct GHOST_XrSwapchain; + +class GHOST_XrSession { + public: + enum LifeExpectancy { + SESSION_KEEP_ALIVE, + SESSION_DESTROY, + }; + + GHOST_XrSession(GHOST_XrContext *xr_context); + ~GHOST_XrSession(); + + void start(const GHOST_XrSessionBeginInfo *begin_info); + void requestEnd(); + + LifeExpectancy handleStateChangeEvent(const XrEventDataSessionStateChanged *lifecycle); + + bool isRunning() const; + + void unbindGraphicsContext(); /* Public so context can ensure it's unbound as needed. */ + + void draw(void *draw_customdata); + + private: + /** Pointer back to context managing this session. Would be nice to avoid, but needed to access + * custom callbacks set before session start. */ + class GHOST_XrContext *m_context; + + std::unique_ptr m_oxr; /* Could use stack, but PImpl is preferable. */ + + /** Active Ghost graphic context. Owned by Blender, not GHOST. */ + class GHOST_Context *m_gpu_ctx = nullptr; + std::unique_ptr m_gpu_binding; + + /** Rendering information. Set when drawing starts. */ + std::unique_ptr m_draw_info; + + void initSystem(); + void end(); + + void bindGraphicsContext(); + + void prepareDrawing(); + XrCompositionLayerProjection drawLayer( + std::vector &r_proj_layer_views, void *draw_customdata); + void drawView(GHOST_XrSwapchain &swapchain, + XrCompositionLayerProjectionView &r_proj_layer_view, + XrView &view, + void *draw_customdata); + void beginFrameDrawing(); + void endFrameDrawing(std::vector *layers); +}; + +#endif /* GHOST_XRSESSION_H__ */ diff --git a/intern/ghost/intern/GHOST_XrSwapchain.cpp b/intern/ghost/intern/GHOST_XrSwapchain.cpp new file mode 100644 index 00000000000..f0b2fb80bf1 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrSwapchain.cpp @@ -0,0 +1,131 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#include + +#include "GHOST_C-api.h" + +#include "GHOST_IXrGraphicsBinding.h" +#include "GHOST_Xr_intern.h" +#include "GHOST_XrException.h" +#include "GHOST_XrSession.h" + +#include "GHOST_XrSwapchain.h" + +struct OpenXRSwapchainData { + using ImageVec = std::vector; + + XrSwapchain swapchain = XR_NULL_HANDLE; + ImageVec swapchain_images; +}; + +static OpenXRSwapchainData::ImageVec swapchain_images_create(XrSwapchain swapchain, + GHOST_IXrGraphicsBinding &gpu_binding) +{ + std::vector images; + uint32_t image_count; + + CHECK_XR(xrEnumerateSwapchainImages(swapchain, 0, &image_count, nullptr), + "Failed to get count of swapchain images to create for the VR session."); + images = gpu_binding.createSwapchainImages(image_count); + CHECK_XR(xrEnumerateSwapchainImages(swapchain, images.size(), &image_count, images[0]), + "Failed to create swapchain images for the VR session."); + + return images; +} + +GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding, + const XrSession &session, + const XrViewConfigurationView &view_config) + : m_oxr(new OpenXRSwapchainData()) +{ + XrSwapchainCreateInfo create_info = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + uint32_t format_count = 0; + int64_t chosen_format; + + CHECK_XR(xrEnumerateSwapchainFormats(session, 0, &format_count, nullptr), + "Failed to get count of swapchain image formats."); + std::vector swapchain_formats(format_count); + CHECK_XR(xrEnumerateSwapchainFormats( + session, swapchain_formats.size(), &format_count, swapchain_formats.data()), + "Failed to get swapchain image formats."); + assert(swapchain_formats.size() == format_count); + + if (!gpu_binding.chooseSwapchainFormat(swapchain_formats, &chosen_format)) { + throw GHOST_XrException( + "Error: No format matching OpenXR runtime supported swapchain formats found."); + } + + create_info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | + XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + create_info.format = chosen_format; + create_info.sampleCount = view_config.recommendedSwapchainSampleCount; + create_info.width = view_config.recommendedImageRectWidth; + create_info.height = view_config.recommendedImageRectHeight; + create_info.faceCount = 1; + create_info.arraySize = 1; + create_info.mipCount = 1; + + CHECK_XR(xrCreateSwapchain(session, &create_info, &m_oxr->swapchain), + "Failed to create OpenXR swapchain."); + + m_image_width = create_info.width; + m_image_height = create_info.height; + + m_oxr->swapchain_images = swapchain_images_create(m_oxr->swapchain, gpu_binding); +} + +GHOST_XrSwapchain::~GHOST_XrSwapchain() +{ + if (m_oxr->swapchain != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroySwapchain(m_oxr->swapchain)); + } +} + +XrSwapchainImageBaseHeader *GHOST_XrSwapchain::acquireDrawableSwapchainImage() + +{ + XrSwapchainImageAcquireInfo acquire_info = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + XrSwapchainImageWaitInfo wait_info = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + uint32_t image_idx; + + CHECK_XR(xrAcquireSwapchainImage(m_oxr->swapchain, &acquire_info, &image_idx), + "Failed to acquire swapchain image for the VR session."); + wait_info.timeout = XR_INFINITE_DURATION; + CHECK_XR(xrWaitSwapchainImage(m_oxr->swapchain, &wait_info), + "Failed to acquire swapchain image for the VR session."); + + return m_oxr->swapchain_images[image_idx]; +} + +void GHOST_XrSwapchain::updateCompositionLayerProjectViewSubImage(XrSwapchainSubImage &r_sub_image) +{ + r_sub_image.swapchain = m_oxr->swapchain; + r_sub_image.imageRect.offset = {0, 0}; + r_sub_image.imageRect.extent = {m_image_width, m_image_height}; +} + +void GHOST_XrSwapchain::releaseImage() +{ + XrSwapchainImageReleaseInfo release_info = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + + CHECK_XR(xrReleaseSwapchainImage(m_oxr->swapchain, &release_info), + "Failed to release swapchain image used to submit VR session frame."); +} diff --git a/intern/ghost/intern/GHOST_XrSwapchain.h b/intern/ghost/intern/GHOST_XrSwapchain.h new file mode 100644 index 00000000000..df9cd924dd0 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrSwapchain.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#ifndef __GHOST_XRSWAPCHAIN_H__ +#define __GHOST_XRSWAPCHAIN_H__ + +#include + +struct OpenXRSwapchainData; + +class GHOST_XrSwapchain { + public: + GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding, + const XrSession &session, + const XrViewConfigurationView &view_config); + ~GHOST_XrSwapchain(); + + XrSwapchainImageBaseHeader *acquireDrawableSwapchainImage(); + void releaseImage(); + + void updateCompositionLayerProjectViewSubImage(XrSwapchainSubImage &r_sub_image); + + private: + std::unique_ptr m_oxr; /* Could use stack, but PImpl is preferable. */ + int32_t m_image_width, m_image_height; +}; + +#endif // GHOST_XRSWAPCHAIN_H diff --git a/intern/ghost/intern/GHOST_Xr_intern.h b/intern/ghost/intern/GHOST_Xr_intern.h new file mode 100644 index 00000000000..d59ffd31940 --- /dev/null +++ b/intern/ghost/intern/GHOST_Xr_intern.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#ifndef __GHOST_XR_INTERN_H__ +#define __GHOST_XR_INTERN_H__ + +#include +#include + +#include "GHOST_Xr_openxr_includes.h" + +#define CHECK_XR(call, error_msg) \ + { \ + XrResult _res = call; \ + if (XR_FAILED(_res)) { \ + throw GHOST_XrException(error_msg, _res); \ + } \ + } \ + (void)0 + +/** + * Variation of CHECK_XR() that doesn't throw, but asserts for success. Especially useful for + * destructors, which shouldn't throw. + */ +#define CHECK_XR_ASSERT(call) \ + { \ + XrResult _res = call; \ + assert(_res == XR_SUCCESS); \ + (void)_res; \ + } \ + (void)0 + +#endif /* __GHOST_XR_INTERN_H__ */ diff --git a/intern/ghost/intern/GHOST_Xr_openxr_includes.h b/intern/ghost/intern/GHOST_Xr_openxr_includes.h new file mode 100644 index 00000000000..925d6037750 --- /dev/null +++ b/intern/ghost/intern/GHOST_Xr_openxr_includes.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + * + * \note This is taken mostly from the OpenXR SDK, but with modified D3D versions (e.g. d3d11_4.h + * -> d3d11.h). Take care for that when updating, we don't want to require newest Win SDKs to be + * installed. + */ + +#ifndef __GHOST_XR_SYSTEM_INCLUDES_H__ +#define __GHOST_XR_SYSTEM_INCLUDES_H__ + +/* Platform headers */ +#ifdef XR_USE_PLATFORM_WIN32 +# define WIN32_LEAN_AND_MEAN +# define NOMINMAX +# include +#endif + +/* Graphics headers */ +#ifdef XR_USE_GRAPHICS_API_D3D10 +# include +#endif +#ifdef XR_USE_GRAPHICS_API_D3D11 +# include +#endif +#ifdef XR_USE_GRAPHICS_API_D3D12 +# include +#endif +#ifdef WITH_X11 +# include +#endif + +#include +#include + +#endif /* __GHOST_XR_SYSTEM_INCLUDES_H__ */ -- cgit v1.2.3