Page MenuHome
Paste P1681

Experiment: Reworked versioning design
ActivePublic

Authored by Julian Eisel (Severin) on Oct 5 2020, 12:29 PM.
diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h
index b14d24e205a..fcad3ab5795 100644
--- a/source/blender/blenkernel/BKE_brush.h
+++ b/source/blender/blenkernel/BKE_brush.h
@@ -157,4 +157,19 @@ void BKE_brush_debug_print_state(struct Brush *br);
#ifdef __cplusplus
}
+
+/* C++ Interface. */
+
+# include <type_traits>
+
+# include "BKE_main.h"
+
+namespace blender::id {
+template<class ID_T>
+std::enable_if_t<std::is_same_v<ID_T, struct ::Brush>, ListBase *> get_libbase(Main &bmain)
+{
+ return &bmain.brushes;
+}
+
+} // namespace blender::id
#endif
diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h
index 20190817a8e..06edb7f39d8 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -262,4 +262,19 @@ void BKE_scene_eval_sequencer_sequences(struct Depsgraph *depsgraph, struct Scen
#ifdef __cplusplus
}
+
+/* C++ Interface. */
+
+# include <type_traits>
+
+# include "BKE_main.h"
+
+namespace blender::id {
+template<class ID_T>
+std::enable_if_t<std::is_same_v<ID_T, struct ::Scene>, ListBase *> get_libbase(Main &bmain)
+{
+ return &bmain.scenes;
+}
+
+} // namespace blender::id
#endif
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index bcc58ecf2c5..31c977c4c4e 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -449,4 +449,19 @@ void BKE_screen_header_alignment_reset(struct bScreen *screen);
#ifdef __cplusplus
}
+
+/* C++ Interface. */
+
+# include <type_traits>
+
+# include "BKE_main.h"
+
+namespace blender::id {
+template<class ID_T>
+std::enable_if_t<std::is_same_v<ID_T, struct ::bScreen>, ListBase *> get_libbase(Main &bmain)
+{
+ return &bmain.screens;
+}
+} // namespace blender::id
+
#endif
diff --git a/source/blender/blenlib/BLI_listbase_wrapper.hh b/source/blender/blenlib/BLI_listbase_wrapper.hh
index c31694d7d9e..6b6c3efef7d 100644
--- a/source/blender/blenlib/BLI_listbase_wrapper.hh
+++ b/source/blender/blenlib/BLI_listbase_wrapper.hh
@@ -56,7 +56,7 @@ template<typename T> class ListBaseWrapper {
Iterator &operator++()
{
- current_ = current_->next;
+ current_ = static_cast<T *>(current_->next);
return *this;
}
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index 992870daeb4..5bf566df31b 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -54,6 +54,7 @@ set(SRC
intern/versioning_270.c
intern/versioning_280.c
intern/versioning_290.c
+ intern/versioning_290.cc
intern/versioning_cycles.c
intern/versioning_defaults.c
intern/versioning_dna.c
@@ -68,6 +69,7 @@ set(SRC
BLO_undofile.h
BLO_writefile.h
intern/readfile.h
+ intern/versioning.h
)
set(LIB
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 0b247d9a249..bb615870c30 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -6940,6 +6940,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
blo_do_versions_270(fd, lib, main);
blo_do_versions_280(fd, lib, main);
blo_do_versions_290(fd, lib, main);
+ blo_do_versions_290_cc(fd, lib, main);
blo_do_versions_cycles(fd, lib, main);
/* WATCH IT!!!: pointers from libdata have not been converted yet here! */
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index d3372a646a9..04e9eae603b 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -195,6 +195,13 @@ void blo_do_versions_260(struct FileData *fd, struct Library *lib, struct Main *
void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_280(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_290(struct FileData *fd, struct Library *lib, struct Main *bmain);
+#ifdef __cplusplus
+extern "C" {
+#endif
+void blo_do_versions_290_cc(struct FileData *fd, struct Library *lib, struct Main *bmain);
+#ifdef __cplusplus
+}
+#endif
void blo_do_versions_cycles(struct FileData *fd, struct Library *lib, struct Main *bmain);
void do_versions_after_linking_250(struct Main *bmain);
diff --git a/source/blender/blenloader/intern/versioning.h b/source/blender/blenloader/intern/versioning.h
new file mode 100644
index 00000000000..723f337ca39
--- /dev/null
+++ b/source/blender/blenloader/intern/versioning.h
@@ -0,0 +1,117 @@
+/*
+ * 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 blenloader
+ */
+
+#pragma once
+
+#include <initializer_list>
+
+#include "BKE_main.h"
+
+/* Helper. Should be placed somewhere else. */
+struct DNAMemberParams {
+ const char *struct_name_;
+ const char *member_type_name_;
+ const char *member_name_;
+
+ DNAMemberParams(const char *struct_name, const char *member_type_name, const char *member_name)
+ : struct_name_(struct_name), member_type_name_(member_type_name), member_name_(member_name)
+ {
+ }
+ template<typename T> DNAMemberParams(std::initializer_list<T>) = delete;
+};
+
+struct VersionNumber {
+ int version_;
+ int subversion_;
+
+ VersionNumber(int version, int subversion) : version_(version), subversion_(subversion)
+ {
+ }
+ template<typename T> VersionNumber(std::initializer_list<T>) = delete;
+};
+
+namespace blender::blenloader::versioning {
+class VersionPatcher {
+ FileData &file_data_;
+ Main &bmain_;
+
+ public:
+ template<class ID_T> using VersionPatchIDFn = std::function<void(Main &, ID_T &)>;
+ using VersionPatchCustomFn = std::function<void()>;
+
+ VersionPatcher(FileData &file_data, Main &bmain) : file_data_(file_data), bmain_(bmain)
+ {
+ }
+
+ /**
+ * If the current version is less then \a version and \subversion, call \a fn for every ID in
+ * main of given template type ID_T.
+ */
+ template<class ID_T> void patch(const VersionNumber &version, VersionPatchIDFn<ID_T> fn)
+ {
+ if (MAIN_VERSION_ATLEAST(&bmain_, version.version_, version.subversion_)) {
+ /* File is new enough. */
+ return;
+ }
+ operator()<ID_T>(fn);
+ }
+
+ template<class ID_T>
+ void patch(const VersionNumber &version,
+ const DNAMemberParams &new_dna_member,
+ VersionPatchIDFn<ID_T> fn)
+ {
+ if (MAIN_VERSION_ATLEAST(&bmain_, version.version_, version.subversion_)) {
+ /* File is new enough. */
+ return;
+ }
+ if (DNA_struct_elem_find(file_data_.filesdna,
+ new_dna_member.struct_name_,
+ new_dna_member.member_type_name_,
+ new_dna_member.member_name_)) {
+ /* File has struct member */
+ return;
+ }
+
+ operator()<ID_T>(fn);
+ }
+
+ void patch(const VersionNumber &version, VersionPatchCustomFn fn)
+ {
+ if (MAIN_VERSION_ATLEAST(&bmain_, version.version_, version.subversion_)) {
+ /* File is new enough. */
+ return;
+ }
+
+ fn();
+ }
+
+ private:
+ template<class ID_T> void operator()(VersionPatchIDFn<ID_T> fn)
+ {
+ ListBase *lb = blender::id::get_libbase<ID_T>(bmain_);
+ for (ID *id_base : ListBaseWrapper<ID>(lb)) {
+ ID_T *id = reinterpret_cast<ID_T *>(id_base);
+ fn(bmain_, *id);
+ }
+ }
+};
+
+} // namespace blender::blenloader::versioning
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 48ae5963da8..5bc2bb286fb 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -358,49 +358,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /** Repair files from duplicate brushes added to blend files, see: T76738. */
- if (!MAIN_VERSION_ATLEAST(bmain, 290, 2)) {
- {
- short id_codes[] = {ID_BR, ID_PAL};
- for (int i = 0; i < ARRAY_SIZE(id_codes); i++) {
- ListBase *lb = which_libbase(bmain, id_codes[i]);
- BKE_main_id_repair_duplicate_names_listbase(lb);
- }
- }
-
- if (!DNA_struct_elem_find(fd->filesdna, "SpaceImage", "float", "uv_opacity")) {
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
- if (sl->spacetype == SPACE_IMAGE) {
- SpaceImage *sima = (SpaceImage *)sl;
- sima->uv_opacity = 1.0f;
- }
- }
- }
- }
- }
-
- /* Init Grease Pencil new random curves. */
- if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "float", "random_hue")) {
- LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
- if ((brush->gpencil_settings) && (brush->gpencil_settings->curve_rand_pressure == NULL)) {
- brush->gpencil_settings->curve_rand_pressure = BKE_curvemapping_add(
- 1, 0.0f, 0.0f, 1.0f, 1.0f);
- brush->gpencil_settings->curve_rand_strength = BKE_curvemapping_add(
- 1, 0.0f, 0.0f, 1.0f, 1.0f);
- brush->gpencil_settings->curve_rand_uv = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
- brush->gpencil_settings->curve_rand_hue = BKE_curvemapping_add(
- 1, 0.0f, 0.0f, 1.0f, 1.0f);
- brush->gpencil_settings->curve_rand_saturation = BKE_curvemapping_add(
- 1, 0.0f, 0.0f, 1.0f, 1.0f);
- brush->gpencil_settings->curve_rand_value = BKE_curvemapping_add(
- 1, 0.0f, 0.0f, 1.0f, 1.0f);
- }
- }
- }
- }
-
if (!MAIN_VERSION_ATLEAST(bmain, 290, 4)) {
/* Clear old deprecated bit-flag from edit weights modifiers, we now use it for something else.
*/
diff --git a/source/blender/blenloader/intern/versioning_290.cc b/source/blender/blenloader/intern/versioning_290.cc
new file mode 100644
index 00000000000..bcc40eb74f2
--- /dev/null
+++ b/source/blender/blenloader/intern/versioning_290.cc
@@ -0,0 +1,93 @@
+/*
+ * 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 blenloader
+ */
+/* allow readfile to use deprecated functionality */
+#define DNA_DEPRECATED_ALLOW
+
+#include <functional>
+
+#include "DNA_brush_types.h"
+#include "DNA_genfile.h"
+
+#include "BLI_array_utils.h"
+#include "BLI_listbase.h"
+#include "BLI_listbase_wrapper.hh"
+#include "BLI_string_ref.hh"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_screen.h"
+
+#include "BLO_readfile.h"
+
+#include "readfile.h"
+#include "versioning.h"
+
+using blender::ListBaseWrapper;
+
+/* Semantic type aliases. */
+using IfLessThanVersion = VersionNumber;
+using IfNoDNAMember = DNAMemberParams;
+
+static void v290_2_gpencil_brushes_new_random_hue(Main &, Brush &brush)
+{
+ if ((brush.gpencil_settings) && (brush.gpencil_settings->curve_rand_pressure == NULL)) {
+ brush.gpencil_settings->curve_rand_pressure = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ brush.gpencil_settings->curve_rand_strength = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ brush.gpencil_settings->curve_rand_uv = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ brush.gpencil_settings->curve_rand_hue = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ brush.gpencil_settings->curve_rand_saturation = BKE_curvemapping_add(
+ 1, 0.0f, 0.0f, 1.0f, 1.0f);
+ brush.gpencil_settings->curve_rand_value = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ }
+}
+
+void blo_do_versions_290_cc(FileData *fd, Library * /*lib*/, Main *bmain)
+{
+ using namespace blender::blenloader::versioning;
+
+ VersionPatcher patcher(*fd, *bmain);
+
+ patcher.patch(IfLessThanVersion(290, 2), [bmain]() {
+ ListBase *lb;
+ lb = which_libbase(bmain, ID_BR);
+ BKE_main_id_repair_duplicate_names_listbase(lb);
+ lb = which_libbase(bmain, ID_PAL);
+ BKE_main_id_repair_duplicate_names_listbase(lb);
+ });
+
+ patcher.patch<bScreen>(IfLessThanVersion(290, 2),
+ IfNoDNAMember("SpaceImage", "float", "uv_opacity"),
+ [](Main &, bScreen &screen) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen.areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_IMAGE) {
+ SpaceImage *sima = (SpaceImage *)sl;
+ sima->uv_opacity = 1.0f;
+ }
+ }
+ }
+ });
+
+ patcher.patch<Brush>(IfLessThanVersion(290, 2),
+ IfNoDNAMember("BrushGpencilSettings", "float", "random_hue"),
+ v290_2_gpencil_brushes_new_random_hue);
+}

Event Timeline

Please see blo_do_versions_290_cc() at the very end to see how this would be used in practice.

Idea was to "sandbox" version patches (right now an accidental return can cause quite some trouble) and avoid some boilerplate (almost each version patch iterates over a main listbase, this could be done automatically by a wrapper). Eventually it should be possible to move version patches to the source files of the type they belong to.

I'm not sure if I like this yet. It looked much better in my head ;)