diff options
author | Hans Goudey <h.goudey@me.com> | 2022-08-14 04:50:06 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-08-14 04:50:06 +0300 |
commit | 6d041a7d510b43a128b3be393a69c2b45ae4885c (patch) | |
tree | 5bcf7e158da5c2d912e0a0e62909664752cdaa3a /source/blender | |
parent | 0018db40a6923d78d879e0ac2f1793c13e9b5737 (diff) | |
parent | b5e92c3dfe70f94556719594c6c1b457cda59768 (diff) |
Merge branch 'master' into refactor-mesh-bevel-weight-generic
Diffstat (limited to 'source/blender')
489 files changed, 17988 insertions, 3197 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 8ba6e7318bb..28c15d9224c 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -151,14 +151,12 @@ add_subdirectory(io) add_subdirectory(functions) add_subdirectory(makesdna) add_subdirectory(makesrna) +add_subdirectory(compositor) if(WITH_BLENDER_THUMBNAILER) add_subdirectory(blendthumb) endif() -if(WITH_COMPOSITOR) - add_subdirectory(compositor) -endif() if(WITH_IMAGE_OPENEXR) add_subdirectory(imbuf/intern/openexr) diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc index 163197c8b67..fff1242f2ce 100644 --- a/source/blender/blendthumb/src/blendthumb_extract.cc +++ b/source/blender/blendthumb/src/blendthumb_extract.cc @@ -136,7 +136,7 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file, thumb->height = bytes_to_native_i32(&shape[4], endian_switch); /* Verify that image dimensions and data size make sense. */ - size_t data_size = block_size - 8; + size_t data_size = block_size - sizeof(shape); const uint64_t expected_size = static_cast<uint64_t>(thumb->width) * static_cast<uint64_t>(thumb->height) * 4; if (thumb->width < 0 || thumb->height < 0 || data_size != expected_size) { diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 17145bdbe99..b3729b7a673 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -17,7 +17,6 @@ #include <ft2build.h> #include FT_FREETYPE_H -#include FT_CACHE_H /* FreeType Cache. */ #include FT_GLYPH_H #include FT_MULTIPLE_MASTERS_H /* Variable font support. */ #include FT_TRUETYPE_IDS_H /* Codepoint coverage constants. */ @@ -56,11 +55,9 @@ BatchBLF g_batch; /* freetype2 handle ONLY for this file! */ static FT_Library ft_lib = NULL; -static FTC_Manager ftc_manager = NULL; -static FTC_CMapCache ftc_charmap_cache = NULL; -static SpinLock ft_lib_mutex; -static SpinLock blf_glyph_cache_mutex; +/* Lock for FreeType library, used around face creation and deletion. */ +static ThreadMutex ft_lib_mutex; /* May be set to #UI_widgetbase_draw_cache_flush. */ static void (*blf_draw_cache_flush)(void) = NULL; @@ -69,50 +66,11 @@ static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font); static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font); /* -------------------------------------------------------------------- */ -/** \name FreeType Caching - * \{ */ - -/* Called when a face is removed. FreeType will call FT_Done_Face itself. */ -static void blf_face_finalizer(void *object) -{ - FT_Face face = object; - FontBLF *font = (FontBLF *)face->generic.data; - font->face = NULL; -} - -/* Called in response to FTC_Manager_LookupFace. Add a face to our font. */ -static FT_Error blf_cache_face_requester(FTC_FaceID faceID, - FT_Library lib, - FT_Pointer UNUSED(reqData), - FT_Face *face) -{ - FontBLF *font = (FontBLF *)faceID; - int err = FT_Err_Cannot_Open_Resource; - - BLI_spin_lock(font->ft_lib_mutex); - - if (font->filepath) { - err = FT_New_Face(lib, font->filepath, 0, face); - } - else if (font->mem) { - err = FT_New_Memory_Face(lib, font->mem, (FT_Long)font->mem_size, 0, face); - } - - BLI_spin_unlock(font->ft_lib_mutex); - - if (err == FT_Err_Ok) { - font->face = *face; - font->face->generic.data = font; - font->face->generic.finalizer = blf_face_finalizer; - } - - return err; -} -/* Use cache, not blf_get_char_index, to return glyph id from charcode. */ +/* Return glyph id from charcode. */ uint blf_get_char_index(struct FontBLF *font, uint charcode) { - return FTC_CMapCache_Lookup(ftc_charmap_cache, font, -1, charcode); + return blf_ensure_face(font) ? FT_Get_Char_Index(font->face, charcode) : 0; } /* -------------------------------------------------------------------- */ @@ -1204,34 +1162,17 @@ char *blf_display_name(FontBLF *font) int blf_font_init(void) { memset(&g_batch, 0, sizeof(g_batch)); - BLI_spin_init(&ft_lib_mutex); - BLI_spin_init(&blf_glyph_cache_mutex); + BLI_mutex_init(&ft_lib_mutex); int err = FT_Init_FreeType(&ft_lib); - if (err == FT_Err_Ok) { - err = FTC_Manager_New(ft_lib, - BLF_CACHE_MAX_FACES, - BLF_CACHE_MAX_SIZES, - BLF_CACHE_BYTES, - blf_cache_face_requester, - NULL, - &ftc_manager); - if (err == FT_Err_Ok) { - err = FTC_CMapCache_New(ftc_manager, &ftc_charmap_cache); - } - } return err; } void blf_font_exit(void) { - BLI_spin_end(&ft_lib_mutex); - if (ftc_manager) { - FTC_Manager_Done(ftc_manager); - } + BLI_mutex_end(&ft_lib_mutex); if (ft_lib) { FT_Done_FreeType(ft_lib); } - BLI_spin_end(&blf_glyph_cache_mutex); blf_batch_draw_exit(); } @@ -1290,8 +1231,6 @@ static void blf_font_fill(FontBLF *font) font->buf_info.col_init[3] = 0; font->ft_lib = ft_lib; - font->ft_lib_mutex = &ft_lib_mutex; - font->glyph_cache_mutex = &blf_glyph_cache_mutex; } /** @@ -1309,7 +1248,14 @@ bool blf_ensure_face(FontBLF *font) FT_Error err; - err = FTC_Manager_LookupFace(ftc_manager, font, &font->face); + BLI_mutex_lock(&ft_lib_mutex); + if (font->filepath) { + err = FT_New_Face(ft_lib, font->filepath, 0, &font->face); + } + if (font->mem) { + err = FT_New_Memory_Face(ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face); + } + BLI_mutex_unlock(&ft_lib_mutex); if (err) { if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) { @@ -1349,6 +1295,7 @@ bool blf_ensure_face(FontBLF *font) } } + font->ft_size = font->face->size; font->face_flags = font->face->face_flags; if (FT_HAS_MULTIPLE_MASTERS(font)) { @@ -1394,12 +1341,12 @@ static const eFaceDetails static_face_details[] = { {"lastresort.woff2", UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX}, {"Noto Sans CJK Regular.woff2", 0x30000083L, 0x2BDF3C10L, 0x16L, 0}, {"NotoEmoji-VariableFont_wght.woff2", 0x80000003L, 0x241E4ACL, 0x14000000L, 0x4000000L}, - {"NotoSansArabic-VariableFont_wdth,wght.woff2", TT_UCR_ARABIC, 0, 0, 0}, - {"NotoSansArmenian-VariableFont_wdth,wght.woff2", - TT_UCR_ARMENIAN, - TT_UCR_ALPHABETIC_PRESENTATION_FORMS, - 0, + {"NotoSansArabic-VariableFont_wdth,wght.woff2", + TT_UCR_ARABIC, + (uint)TT_UCR_ARABIC_PRESENTATION_FORMS_A, + TT_UCR_ARABIC_PRESENTATION_FORMS_B, 0}, + {"NotoSansArmenian-VariableFont_wdth,wght.woff2", TT_UCR_ARMENIAN, 0, 0, 0}, {"NotoSansBengali-VariableFont_wdth,wght.woff2", TT_UCR_BENGALI, 0, 0, 0}, {"NotoSansDevanagari-Regular.woff2", TT_UCR_DEVANAGARI, 0, 0, 0}, {"NotoSansEthiopic-Regular.woff2", 0, 0, TT_UCR_ETHIOPIC, 0}, @@ -1435,6 +1382,8 @@ static FontBLF *blf_font_new_ex(const char *name, } blf_font_fill(font); + BLI_mutex_init(&font->glyph_cache_mutex); + /* If we have static details about this font we don't need to load the Face. */ const eFaceDetails *static_details = NULL; char filename[256]; @@ -1501,7 +1450,9 @@ void blf_font_free(FontBLF *font) } if (font->face) { - FTC_Manager_RemoveFaceID(ftc_manager, font); + BLI_mutex_lock(&ft_lib_mutex); + FT_Done_Face(font->face); + BLI_mutex_unlock(&ft_lib_mutex); font->face = NULL; } if (font->filepath) { @@ -1510,6 +1461,9 @@ void blf_font_free(FontBLF *font) if (font->name) { MEM_freeN(font->name); } + + BLI_mutex_end(&font->glyph_cache_mutex); + MEM_freeN(font); } @@ -1530,23 +1484,17 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi) /* Adjust our new size to be on even 64ths. */ size = (float)ft_size / 64.0f; - FTC_ScalerRec scaler = {0}; - scaler.face_id = font; - scaler.width = 0; - scaler.height = ft_size; - scaler.pixel = 0; - scaler.x_res = dpi; - scaler.y_res = dpi; - - if (FTC_Manager_LookupSize(ftc_manager, &scaler, &font->ft_size) != FT_Err_Ok) { - printf("The current font don't support the size, %f and dpi, %u\n", size, dpi); - return false; + if (font->size != size || font->dpi != dpi) { + if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == FT_Err_Ok) { + font->size = size; + font->dpi = dpi; + } + else { + printf("The current font does not support the size, %f and DPI, %u\n", size, dpi); + return false; + } } - font->size = size; - font->dpi = dpi; - font->ft_size->generic.data = (void *)font; - return true; } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 780b75c6296..f938174f92e 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -115,7 +115,7 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font) { - BLI_spin_lock(font->glyph_cache_mutex); + BLI_mutex_lock(&font->glyph_cache_mutex); GlyphCacheBLF *gc = blf_glyph_cache_find(font, font->size, font->dpi); @@ -128,7 +128,7 @@ GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font) void blf_glyph_cache_release(FontBLF *font) { - BLI_spin_unlock(font->glyph_cache_mutex); + BLI_mutex_unlock(&font->glyph_cache_mutex); } static void blf_glyph_cache_free(GlyphCacheBLF *gc) @@ -152,13 +152,13 @@ void blf_glyph_cache_clear(FontBLF *font) { GlyphCacheBLF *gc; - BLI_spin_lock(font->glyph_cache_mutex); + BLI_mutex_lock(&font->glyph_cache_mutex); while ((gc = BLI_pophead(&font->cache))) { blf_glyph_cache_free(gc); } - BLI_spin_unlock(font->glyph_cache_mutex); + BLI_mutex_unlock(&font->glyph_cache_mutex); } /** @@ -787,8 +787,8 @@ static bool blf_glyph_transform_weight(FT_GlyphSlot glyph, float factor, bool mo { if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { /* Fake bold if the font does not have this variable axis. */ - const FontBLF *font = (FontBLF *)glyph->face->generic.data; - const FT_Pos average_width = font->ft_size->metrics.height; + const FT_Pos average_width = FT_MulFix(glyph->face->units_per_EM, + glyph->face->size->metrics.x_scale); FT_Pos change = (FT_Pos)((float)average_width * factor * 0.1f); FT_Outline_EmboldenXY(&glyph->outline, change, change / 2); if (monospaced) { @@ -847,8 +847,7 @@ static bool blf_glyph_transform_width(FT_GlyphSlot glyph, float factor) static bool blf_glyph_transform_spacing(FT_GlyphSlot glyph, float factor) { if (glyph->advance.x > 0) { - const FontBLF *font = (FontBLF *)glyph->face->generic.data; - const long int size = font->ft_size->metrics.height; + const long int size = glyph->face->size->metrics.height; glyph->advance.x += (FT_Pos)(factor * (float)size / 6.0f); return true; } diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 8ff00d05e02..221e656f096 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -16,14 +16,7 @@ struct rcti; /* Max number of FontBLFs in memory. Take care that every font has a glyph cache per size/dpi, * so we don't need load the same font with different size, just load one and call BLF_size. */ -#define BLF_MAX_FONT 64 - -/* Maximum number of opened FT_Face objects managed by cache. 0 is default of 2. */ -#define BLF_CACHE_MAX_FACES 0 -/* Maximum number of opened FT_Size objects managed by cache. 0 is default of 4 */ -#define BLF_CACHE_MAX_SIZES 0 -/* Maximum number of bytes to use for cached data nodes. 0 is default of 200,000. */ -#define BLF_CACHE_BYTES 0 +#define BLF_MAX_FONT 32 extern struct FontBLF *global_font[BLF_MAX_FONT]; diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 007b717ab93..dfe24c1aa47 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -323,13 +323,10 @@ typedef struct FontBLF { /* freetype2 lib handle. */ FT_Library ft_lib; - /* Mutex lock for library */ - SpinLock *ft_lib_mutex; - /* freetype2 face. */ FT_Face face; - /* FreeType size is separated from face when using their caching subsystem. */ + /* Point to face->size or to cache's size. */ FT_Size ft_size; /* Copy of the font->face->face_flags, in case we don't have a face loaded. */ @@ -339,7 +336,7 @@ typedef struct FontBLF { FontBufInfoBLF buf_info; /* Mutex lock for glyph cache. */ - SpinLock *glyph_cache_mutex; + ThreadMutex glyph_cache_mutex; } FontBLF; typedef struct DirBLF { diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 568899721a9..fc8a00af4a1 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -97,7 +97,7 @@ class CurvesGeometryRuntime { mutable Span<float3> evaluated_positions_span; /** - * Cache of lengths along each evaluated curve for for each evaluated point. If a curve is + * Cache of lengths along each evaluated curve for each evaluated point. If a curve is * cyclic, it needs one more length value to correspond to the last segment, so in order to * make slicing this array for a curve fast, an extra float is stored for every curve. */ diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index be8823c5ad2..d36e6031557 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -11,7 +11,9 @@ #include "BLI_sys_types.h" #include "BLI_utildefines.h" #ifdef __cplusplus +# include "BLI_set.hh" # include "BLI_span.hh" +# include "BLI_string_ref.hh" # include "BLI_vector.hh" #endif @@ -141,6 +143,15 @@ void CustomData_copy(const struct CustomData *source, eCDAllocType alloctype, int totelem); +/** + * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh. + */ +void CustomData_copy_mesh_to_bmesh(const struct CustomData *source, + struct CustomData *dest, + eCustomDataMask mask, + eCDAllocType alloctype, + int totelem); + /* BMESH_TODO, not really a public function but readfile.c needs it */ void CustomData_update_typemap(struct CustomData *data); @@ -155,6 +166,15 @@ bool CustomData_merge(const struct CustomData *source, int totelem); /** + * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh. + */ +bool CustomData_merge_mesh_to_bmesh(const struct CustomData *source, + struct CustomData *dest, + eCustomDataMask mask, + eCDAllocType alloctype, + int totelem); + +/** * Reallocate custom data to a new element count. * Only affects on data layers which are owned by the CustomData itself, * referenced data is kept unchanged, @@ -696,7 +716,8 @@ void CustomData_data_transfer(const struct MeshPairRemap *me_remap, * the struct. */ void CustomData_blend_write_prepare(CustomData &data, - blender::Vector<CustomDataLayer, 16> &layers_to_write); + blender::Vector<CustomDataLayer, 16> &layers_to_write, + const blender::Set<blender::StringRef> &skip_names = {}); /** * \param layers_to_write: Layers created by #CustomData_blend_write_prepare. diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index be2ec3e3dca..a5c4d5e1365 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -505,7 +505,7 @@ class CurveComponentLegacy : public GeometryComponent { /** * A geometry component that stores a group of curves, corresponding the #Curves data-block type - * and the #CurvesGeometry type. Attributes are are stored on the control point domain and the + * and the #CurvesGeometry type. Attributes are stored on the control point domain and the * curve domain. */ class CurveComponent : public GeometryComponent { diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 30f7ed45859..95f4c8f6dce 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -85,7 +85,11 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id, typedef void (*IDTypeForeachPathFunction)(struct ID *id, struct BPathForeachPathData *bpath_data); -typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct Main *bmain, struct ID *id); +/** \param owner_id_hint: If non-NULL, a potential owner of the given embedded ID. Can speed up + * look-up of the owner ID in some cases. */ +typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct Main *bmain, + struct ID *id, + struct ID *owner_id_hint); typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer, struct ID *id, @@ -109,7 +113,7 @@ typedef struct IDTypeInfo { */ short id_code; /** - * Bitflag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's + * Bit-flag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's * FILTER_ID_XX enums. */ uint64_t id_filter; diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index 37b30d63722..9f506ded8e9 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -47,7 +47,7 @@ void key_curve_normal_weights(float t, float data[4], int type); /** * Returns key coordinates (+ tilt) when key applied, NULL otherwise. * - * \param obdata if given, also update that geometry with the result of the shape keys evaluation. + * \param obdata: if given, also update that geometry with the result of the shape keys evaluation. */ float *BKE_key_evaluate_object_ex( struct Object *ob, int *r_totelem, float *arr, size_t arr_size, struct ID *obdata); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 148e6ec2791..febdad2ca0d 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -444,18 +444,20 @@ struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id); * Currently, it only handles the given ID, and their shape keys and actions if any, according to * the given `duplicate_flags`. * - * \param duplicate_flags is of type #eDupli_ID_Flags, see #UserDef.dupflag. Currently only + * \param duplicate_flags: is of type #eDupli_ID_Flags, see #UserDef.dupflag. Currently only * `USER_DUP_LINKED_ID` and `USER_DUP_ACT` have an effect here. - * \param copy_flags flags passed to #BKE_id_copy_ex. + * \param copy_flags: flags passed to #BKE_id_copy_ex. */ struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, struct ID *id, uint duplicate_flags, int copy_flags); -/* Special version of BKE_id_copy which is safe from using evaluated id as source with a copy +/** + * Special version of #BKE_id_copy which is safe from using evaluated id as source with a copy * result appearing in the main database. - * Takes care of the referenced data-blocks consistency. */ + * Takes care of the referenced data-blocks consistency. + */ struct ID *BKE_id_copy_for_use_in_bmain(struct Main *bmain, const struct ID *id); /** diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index f933946164c..8542c02fab5 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -66,10 +66,12 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, bool do_ * \note This is especially useful when `id` is a non-real override (e.g. embedded ID like a master * collection or root node tree, or a shape key). * - * \param r_owner_id If given, will be set with the actual ID owning the return liboverride data. + * \param owner_id_hint: If not NULL, a potential owner for the given override-embedded `id`. + * \param r_owner_id: If given, will be set with the actual ID owning the return liboverride data. */ IDOverrideLibrary *BKE_lib_override_library_get(struct Main *bmain, struct ID *id, + struct ID *owner_id_hint, struct ID **r_owner_id); /** diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 17f541b362e..b9f6b4b73f3 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -853,7 +853,7 @@ struct Mesh *BKE_mesh_merge_verts(struct Mesh *mesh, int merge_mode); /** - * Account for custom-data such as UV's becoming detached because of of imprecision + * Account for custom-data such as UV's becoming detached because of imprecision * in custom-data interpolation. * Without running this operation subdivision surface can cause UV's to be disconnected, * see: T81065. @@ -865,19 +865,7 @@ void BKE_mesh_merge_customdata_for_apply_modifier(struct Mesh *me); /** * Update the hide flag for edges and faces from the corresponding flag in verts. */ -void BKE_mesh_flush_hidden_from_verts_ex(const struct MVert *mvert, - const struct MLoop *mloop, - struct MEdge *medge, - int totedge, - struct MPoly *mpoly, - int totpoly); void BKE_mesh_flush_hidden_from_verts(struct Mesh *me); -void BKE_mesh_flush_hidden_from_polys_ex(struct MVert *mvert, - const struct MLoop *mloop, - struct MEdge *medge, - int totedge, - const struct MPoly *mpoly, - int totpoly); void BKE_mesh_flush_hidden_from_polys(struct Mesh *me); /** * simple poly -> vert/edge selection. diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.h b/source/blender/blenkernel/BKE_mesh_legacy_convert.h index 208f0877e11..7dcfc8379ef 100644 --- a/source/blender/blenkernel/BKE_mesh_legacy_convert.h +++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.h @@ -18,6 +18,7 @@ struct Mesh; struct MFace; /** +<<<<<<< HEAD * Copy bevel weights from separate layers into vertices and edges. */ void BKE_mesh_legacy_bevel_weight_from_layers(struct Mesh *mesh); @@ -25,6 +26,16 @@ void BKE_mesh_legacy_bevel_weight_from_layers(struct Mesh *mesh); * Copy bevel weights from vertices and edges to separate layers. */ void BKE_mesh_legacy_bevel_weight_to_layers(struct Mesh *mesh); +======= + * Convert the hidden element attributes to the old flag format for writing. + */ +void BKE_mesh_legacy_convert_hide_layers_to_flags(struct Mesh *mesh); +/** + * Convert the old hide flags (#ME_HIDE) to the hidden element attribute for reading. + * Only add the attributes when there are any elements in each domain hidden. + */ +void BKE_mesh_legacy_convert_flags_to_hide_layers(struct Mesh *mesh); +>>>>>>> master /** * Recreate #MFace Tessellation. diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index 44588a06119..abe590b6806 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -20,7 +20,7 @@ struct MVert; /* UvVertMap */ #define STD_UV_CONNECT_LIMIT 0.0001f -/* Map from uv vertex to face. Used by select linked, uv subsurf and obj exporter. */ +/* Map from uv vertex to face. Used by select linked, uv subdivision-surface and obj exporter. */ typedef struct UvVertMap { struct UvMapVert **vert; struct UvMapVert *buf; @@ -68,13 +68,20 @@ typedef struct UvElementMap { /** Total number of unique UVs. */ int total_unique_uvs; - /* If Non-NULL, address UvElements by `BM_elem_index_get(BMVert*)`. */ + /** If Non-NULL, address UvElements by `BM_elem_index_get(BMVert*)`. */ struct UvElement **vertex; - /* Number of Islands in the mesh */ - int totalIslands; - /* Stores the starting index in buf where each island begins */ - int *islandIndices; + /** If Non-NULL, pointer to local head of each unique UV. */ + struct UvElement **head_table; + + /** Number of islands, or zero if not calculated. */ + int total_islands; + /** Array of starting index in #storage where each island begins. */ + int *island_indices; + /** Array of number of UVs in each island. */ + int *island_total_uvs; + /** Array of number of unique UVs in each island. */ + int *island_total_unique_uvs; } UvElementMap; /* Connectivity data */ @@ -85,6 +92,7 @@ typedef struct MeshElemMap { /* mapping */ UvVertMap *BKE_mesh_uv_vert_map_create(const struct MPoly *mpoly, + const bool *hide_poly, const struct MLoop *mloop, const struct MLoopUV *mloopuv, unsigned int totpoly, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 02e3cefe6a5..b42b9df510d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -101,6 +101,7 @@ typedef struct bNodeSocketTemplate { namespace blender { class CPPType; namespace nodes { +class DNode; class NodeMultiFunctionBuilder; class GeoNodeExecParams; class NodeDeclarationBuilder; @@ -109,6 +110,11 @@ class GatherLinkSearchOpParams; namespace fn { class MFDataType; } // namespace fn +namespace realtime_compositor { +class Context; +class NodeOperation; +class ShaderNode; +} // namespace realtime_compositor } // namespace blender using CPPTypeHandle = blender::CPPType; @@ -123,7 +129,14 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket using NodeGatherSocketLinkOperationsFunction = void (*)(blender::nodes::GatherLinkSearchOpParams ¶ms); +using NodeGetCompositorOperationFunction = blender::realtime_compositor::NodeOperation + *(*)(blender::realtime_compositor::Context &context, blender::nodes::DNode node); +using NodeGetCompositorShaderNodeFunction = + blender::realtime_compositor::ShaderNode *(*)(blender::nodes::DNode node); + #else +typedef void *NodeGetCompositorOperationFunction; +typedef void *NodeGetCompositorShaderNodeFunction; typedef void *NodeMultiFunctionBuildFunction; typedef void *NodeGeometryExecFunction; typedef void *NodeDeclareFunction; @@ -309,6 +322,14 @@ typedef struct bNodeType { /* gpu */ NodeGPUExecFunction gpu_fn; + /* Get an instance of this node's compositor operation. Freeing the instance is the + * responsibility of the caller. */ + NodeGetCompositorOperationFunction get_compositor_operation; + + /* Get an instance of this node's compositor shader node. Freeing the instance is the + * responsibility of the caller. */ + NodeGetCompositorShaderNodeFunction get_compositor_shader_node; + /* Build a multi-function for this node. */ NodeMultiFunctionBuildFunction build_multi_function; diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index f0eb16a819d..8f3b488c7db 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -586,7 +586,6 @@ void BKE_object_runtime_reset_on_copy(struct Object *object, int flag); void BKE_object_runtime_free_data(struct Object *object); void BKE_object_batch_cache_dirty_tag(struct Object *ob); -void BKE_object_data_batch_cache_dirty_tag(struct ID *object_data); /* this function returns a superset of the scenes selection based on relationships */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 8b9deadc960..fa67ff08383 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -213,7 +213,7 @@ bool BKE_paint_always_hide_test(struct Object *ob); * Returns non-zero if any of the face's vertices are hidden, zero otherwise. */ bool paint_is_face_hidden(const struct MLoopTri *lt, - const struct MVert *mvert, + const bool *hide_vert, const struct MLoop *mloop); /** * Returns non-zero if any of the corners of the grid diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index af7effe806c..2be3f323d07 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -533,6 +533,7 @@ typedef struct PBVHVertexIter { /* mesh */ struct MVert *mverts; float (*vert_normals)[3]; + const bool *hide_vert; int totvert; const int *vert_indices; float *vmask; @@ -593,7 +594,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m else if (vi.mverts) { \ vi.mvert = &vi.mverts[vi.vert_indices[vi.gx]]; \ if (vi.respect_hide) { \ - vi.visible = !(vi.mvert->flag & ME_HIDE); \ + vi.visible = !(vi.hide_vert && vi.hide_vert[vi.vert_indices[vi.gx]]); \ if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \ continue; \ } \ @@ -667,6 +668,8 @@ void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings, struct MVert *BKE_pbvh_get_verts(const PBVH *pbvh); const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3]; +const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh); +bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh); PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node); void BKE_pbvh_node_color_buffer_free(PBVH *pbvh); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index f76f7f5a968..e3f00d03a3b 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -191,7 +191,7 @@ set(SRC intern/mask_evaluate.c intern/mask_rasterize.c intern/material.c - intern/mball.c + intern/mball.cc intern/mball_tessellate.c intern/mesh.cc intern/mesh_boolean_convert.cc diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index a29d8726f21..2ce5863c176 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -1595,7 +1595,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, /* Add orco coordinates to final and deformed mesh if requested. */ if (final_datamask.vmask & CD_MASK_ORCO) { - /* FIXME(Campbell): avoid the need to convert to mesh data just to add an orco layer. */ + /* FIXME(@campbellbarton): avoid the need to convert to mesh data just to add an orco layer. */ BKE_mesh_wrapper_ensure_mdata(mesh_final); add_orco_mesh(ob, em_input, mesh_final, mesh_orco, CD_ORCO); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 1af3cde1821..b9995796a21 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -56,7 +56,7 @@ const char *no_procedural_access_message = bool allow_procedural_attribute_access(StringRef attribute_name) { - return !attribute_name.startswith(".selection"); + return !attribute_name.startswith(".selection") && !attribute_name.startswith(".hide"); } static int attribute_data_type_complexity(const eCustomDataType data_type) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 99733c8edb3..fc45ce0bbe7 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -597,7 +597,7 @@ using eGPCurveMappingPreset = enum eGPCurveMappingPreset { GPCURVE_PRESET_CHISEL_STRENGTH = 5, }; -static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) +static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, eGPCurveMappingPreset preset) { if (cuma->curve) { MEM_freeN(cuma->curve); @@ -2512,8 +2512,10 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool if (display_gradient || have_texture) { for (int i = 0; i < side; i++) { for (int j = 0; j < side; j++) { - float magn = sqrtf(pow2f(i - half) + pow2f(j - half)); - im->rect_float[i * side + j] *= BKE_brush_curve_strength_clamped(br, magn, half); + const float magn = sqrtf(pow2f(i - half) + pow2f(j - half)); + const float strength = BKE_brush_curve_strength_clamped(br, magn, half); + im->rect_float[i * side + j] = (have_texture) ? im->rect_float[i * side + j] * strength : + strength; } } } diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 03dd5c89b70..d0b57b45d35 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -27,6 +27,8 @@ #include "MEM_guardedalloc.h" +using blender::VArray; + /* -------------------------------------------------------------------- */ /** \name BVHCache * \{ */ @@ -1181,9 +1183,13 @@ static BLI_bitmap *loose_edges_map_get(const MEdge *medge, } static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly, + const VArray<bool> &hide_poly, const int looptri_len, int *r_looptri_active_len) { + if (hide_poly.is_single() && !hide_poly.get_internal_single()) { + return nullptr; + } BLI_bitmap *looptri_mask = BLI_BITMAP_NEW(looptri_len, __func__); int looptri_no_hidden_len = 0; @@ -1191,8 +1197,7 @@ static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly, int i_poly = 0; while (looptri_iter != looptri_len) { int mp_totlooptri = mpoly[i_poly].totloop - 2; - const MPoly &mp = mpoly[i_poly]; - if (mp.flag & ME_HIDE) { + if (hide_poly[i_poly]) { looptri_iter += mp_totlooptri; } else { @@ -1276,9 +1281,15 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, 0.0f, tree_type, 6, mesh->mvert, mesh->mface, mesh->totface, nullptr, -1); break; - case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: - mask = looptri_no_hidden_map_get(mesh->mpoly, looptri_len, &mask_bits_act_len); + case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: { + blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*mesh); + mask = looptri_no_hidden_map_get( + mesh->mpoly, + attributes.lookup_or_default(".hide_poly", ATTR_DOMAIN_FACE, false), + looptri_len, + &mask_bits_act_len); ATTR_FALLTHROUGH; + } case BVHTREE_FROM_LOOPTRI: data->tree = bvhtree_from_mesh_looptri_create_tree(0.0f, tree_type, diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 934c3053a02..4967e3482c6 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -162,7 +162,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) } } -static ID *collection_owner_get(Main *bmain, ID *id) +static ID *collection_owner_get(Main *bmain, ID *id, ID *owner_id_hint) { if ((id->flag & LIB_EMBEDDED_DATA) == 0) { return id; @@ -172,6 +172,11 @@ static ID *collection_owner_get(Main *bmain, ID *id) Collection *master_collection = (Collection *)id; BLI_assert((master_collection->flag & COLLECTION_IS_MASTER) != 0); + if (owner_id_hint != NULL && GS(owner_id_hint->name) == ID_SCE && + ((Scene *)owner_id_hint)->master_collection == master_collection) { + return owner_id_hint; + } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (scene->master_collection == master_collection) { return &scene->id; diff --git a/source/blender/blenkernel/intern/colorband.c b/source/blender/blenkernel/intern/colorband.c index b2f817b7821..5145f1cbbc0 100644 --- a/source/blender/blenkernel/intern/colorband.c +++ b/source/blender/blenkernel/intern/colorband.c @@ -144,7 +144,7 @@ static float color_sample_remove_cost(const struct ColorResampleElem *c) return area; } -/* TODO(campbell): create BLI_math_filter? */ +/* TODO(@campbellbarton): create `BLI_math_filter` ? */ static float filter_gauss(float x) { const float gaussfac = 1.6f; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 9f1d2d7bbb2..071fa5fe6bf 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1559,7 +1559,7 @@ static void followpath_evaluate(bConstraint *con, bConstraintOb *cob, ListBase * /* un-apply scaling caused by path */ if ((data->followflag & FOLLOWPATH_RADIUS) == 0) { - /* XXX(campbell): Assume that scale correction means that radius + /* XXX(@campbellbarton): Assume that scale correction means that radius * will have some scale error in it. */ float obsize[3]; diff --git a/source/blender/blenkernel/intern/cryptomatte_test.cc b/source/blender/blenkernel/intern/cryptomatte_test.cc index 2f15242b4a4..bb09b276645 100644 --- a/source/blender/blenkernel/intern/cryptomatte_test.cc +++ b/source/blender/blenkernel/intern/cryptomatte_test.cc @@ -163,7 +163,7 @@ TEST(cryptomatte, session_from_stamp_data) * best as possible. */ TEST(cryptomatte, parsing_malformed_manifests) { - /* Manifest from multilayer.exr in the cryptomatte git-repository. */ + /* Manifest from `multilayer.exr` in the cryptomatte git-repository. */ test_cryptomatte_manifest( R"({"/obj/instance1:instances:0":"0d54c6cc","/obj/instance1:instances:1":"293d9340","/obj/instance1:instances:110":"ccb9e1f2","/obj/instance1:instances:111":"f8dd3a48","/obj/instance1:instances:112":"a99e07a8","/obj/instance1:instances:113":"e75599a4","/obj/instance1:instances:114":"794200f3","/obj/instance1:instances:115":"2a3a1728","/obj/instance1:instances:116":"478544a1","/obj/instance1:instances:117":"b2bd969a","/obj/instance1:instances:10":"3a0c8681","/obj/instance1:instances:11":"01e5970d","/obj/box:polygons:1":"9d416418","/obj/instance1:instances:100":"2dcd2966","/obj/instance1:instances:101":"9331cd82","/obj/instance1:instances:102":"df50fccb","/obj/instance1:instances:103":"97f8590d","/obj/instance1:instances:104":"bbcd220d","/obj/instance1:instances:105":"4ae06139","/obj/instance1:instances:106":"8873d5ea","/obj/instance1:instances:107":"39d8af8d","/obj/instance1:instances:108":"bb11bd4e","/obj/instance1:instances:109":"a32bba35"})", R"({"\/obj\/box:polygons:1":"9d416418","\/obj\/instance1:instances:0":"0d54c6cc","\/obj\/instance1:instances:1":"293d9340","\/obj\/instance1:instances:10":"3a0c8681","\/obj\/instance1:instances:100":"2dcd2966","\/obj\/instance1:instances:101":"9331cd82","\/obj\/instance1:instances:102":"df50fccb","\/obj\/instance1:instances:103":"97f8590d","\/obj\/instance1:instances:104":"bbcd220d","\/obj\/instance1:instances:105":"4ae06139","\/obj\/instance1:instances:106":"8873d5ea","\/obj\/instance1:instances:107":"39d8af8d","\/obj\/instance1:instances:108":"bb11bd4e","\/obj\/instance1:instances:109":"a32bba35","\/obj\/instance1:instances:11":"01e5970d","\/obj\/instance1:instances:110":"ccb9e1f2","\/obj\/instance1:instances:111":"f8dd3a48","\/obj\/instance1:instances:112":"a99e07a8","\/obj\/instance1:instances:113":"e75599a4","\/obj\/instance1:instances:114":"794200f3","\/obj\/instance1:instances:115":"2a3a1728","\/obj\/instance1:instances:116":"478544a1","\/obj\/instance1:instances:117":"b2bd969a","\/obj\/instance1:instance)"); diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 5125e010b81..40b64aa8dc8 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -2470,7 +2470,7 @@ static void make_bevel_list_segment_2D(BevList *bl) static void make_bevel_list_2D(BevList *bl) { - /* NOTE(campbell): `bevp->dir` and `bevp->quat` are not needed for beveling but are + /* NOTE(@campbellbarton): `bevp->dir` and `bevp->quat` are not needed for beveling but are * used when making a path from a 2D curve, therefore they need to be set. */ BevPoint *bevp0, *bevp1, *bevp2; diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index c37634b5d3d..6fdcb56fc91 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1190,7 +1190,7 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, }, [&]() { /* Copy over curve attributes. - * In some cases points are just dissolved, so the the number of + * In some cases points are just dissolved, so the number of * curves will be the same. That could be optimized in the future. */ for (bke::AttributeTransferData &attribute : curve_attributes) { if (new_curves.curves_num() == curves.curves_num()) { diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index bc3c6a8269d..6c6160ac0f1 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -26,6 +26,7 @@ #include "BLI_math_vector.hh" #include "BLI_mempool.h" #include "BLI_path_util.h" +#include "BLI_set.hh" #include "BLI_span.hh" #include "BLI_string.h" #include "BLI_string_ref.hh" @@ -58,6 +59,7 @@ #include "data_transfer_intern.h" using blender::IndexRange; +using blender::Set; using blender::Span; using blender::StringRef; using blender::Vector; @@ -2342,6 +2344,43 @@ bool CustomData_merge(const CustomData *source, return changed; } +static bool attribute_stored_in_bmesh_flag(const StringRef name) +{ + return ELEM(name, ".hide_vert", ".hide_edge", ".hide_poly"); +} + +static CustomData shallow_copy_remove_non_bmesh_attributes(const CustomData &src) +{ + Vector<CustomDataLayer> dst_layers; + for (const CustomDataLayer &layer : Span<CustomDataLayer>{src.layers, src.totlayer}) { + if (!attribute_stored_in_bmesh_flag(layer.name)) { + dst_layers.append(layer); + } + } + + CustomData dst = src; + dst.layers = static_cast<CustomDataLayer *>( + MEM_calloc_arrayN(dst_layers.size(), sizeof(CustomDataLayer), __func__)); + dst.totlayer = dst_layers.size(); + memcpy(dst.layers, dst_layers.data(), dst_layers.as_span().size_in_bytes()); + + CustomData_update_typemap(&dst); + + return dst; +} + +bool CustomData_merge_mesh_to_bmesh(const CustomData *source, + CustomData *dest, + const eCustomDataMask mask, + const eCDAllocType alloctype, + const int totelem) +{ + CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source); + const bool result = CustomData_merge(&source_copy, dest, mask, alloctype, totelem); + MEM_SAFE_FREE(source_copy.layers); + return result; +} + void CustomData_realloc(CustomData *data, const int totelem) { BLI_assert(totelem >= 0); @@ -2373,6 +2412,17 @@ void CustomData_copy(const CustomData *source, CustomData_merge(source, dest, mask, alloctype, totelem); } +void CustomData_copy_mesh_to_bmesh(const CustomData *source, + CustomData *dest, + const eCustomDataMask mask, + const eCDAllocType alloctype, + const int totelem) +{ + CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source); + CustomData_copy(&source_copy, dest, mask, alloctype, totelem); + MEM_SAFE_FREE(source_copy.layers); +} + static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem) { const LayerTypeInfo *typeInfo; @@ -4303,7 +4353,9 @@ void CustomData_file_write_info(int type, const char **r_struct_name, int *r_str *r_struct_num = typeInfo->structnum; } -void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16> &layers_to_write) +void CustomData_blend_write_prepare(CustomData &data, + Vector<CustomDataLayer, 16> &layers_to_write, + const Set<StringRef> &skip_names) { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { if (layer.flag & CD_FLAG_NOCOPY) { @@ -4312,6 +4364,9 @@ void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16 if (layer.anonymous_id != nullptr) { continue; } + if (skip_names.contains(layer.name)) { + continue; + } layers_to_write.append(layer); } data.totlayer = layers_to_write.size(); diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index b1017a78e15..0b3ed584246 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -1480,7 +1480,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, * - The dependency graph has handling of edit mode pointers (see #update_edit_mode_pointers) * but it doesn't seem to work in this case. * - * Since the the plan is to replace this legacy curve object with the curves data-block + * Since the plan is to replace this legacy curve object with the curves data-block * (see T95355), this somewhat hacky inefficient solution is relatively temporary. */ Curve &cow_curve = *reinterpret_cast<Curve *>( diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index d68b322e4c5..bf224a9613e 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -337,7 +337,6 @@ static void gpencil_convert_spline(Main *bmain, /* Add stroke to frame. */ BLI_addtail(&gpf->strokes, gps); - float *coord_array = NULL; float init_co[3]; switch (nu->type) { @@ -376,8 +375,7 @@ static void gpencil_convert_spline(Main *bmain, BezTriple *bezt = &nu->bezt[inext]; bool last = (bool)(s == segments - 1); - coord_array = MEM_callocN((size_t)3 * resolu * sizeof(float), __func__); - + float *coord_array = MEM_callocN(sizeof(float[3]) * resolu, __func__); for (int j = 0; j < 3; j++) { BKE_curve_forward_diff_bezier(prevbezt->vec[1][j], prevbezt->vec[2][j], @@ -397,8 +395,9 @@ static void gpencil_convert_spline(Main *bmain, gpencil_add_new_points( gps, coord_array, radius_start, radius_end, init, resolu, init_co, last); + /* Free memory. */ - MEM_SAFE_FREE(coord_array); + MEM_freeN(coord_array); /* As the last point of segment is the first point of next segment, back one array * element to avoid duplicated points on the same location. @@ -419,7 +418,7 @@ static void gpencil_convert_spline(Main *bmain, nurb_points = (nu->pntsu - 1) * resolu; } /* Get all curve points. */ - coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__); + float *coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__); BKE_nurb_makeCurve(nu, coord_array, NULL, NULL, NULL, resolu, sizeof(float[3])); /* Allocate memory for storage points. */ @@ -429,7 +428,7 @@ static void gpencil_convert_spline(Main *bmain, /* Add points. */ gpencil_add_new_points(gps, coord_array, 1.0f, 1.0f, 0, gps->totpoints, init_co, false); - MEM_SAFE_FREE(coord_array); + MEM_freeN(coord_array); } break; } diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 82899b974bc..8ac268b26b0 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -360,7 +360,8 @@ GpencilModifierData *BKE_gpencil_modifier_new(int type) md->type = type; md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render; md->flag = eGpencilModifierFlag_OverrideLibrary_Local; - md->ui_expand_flag = 1; /* Only expand the parent panel at first. */ + /* Only expand the parent panel at first. */ + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) { md->mode |= eGpencilModifierMode_Editmode; diff --git a/source/blender/blenkernel/intern/icons_rasterize.c b/source/blender/blenkernel/intern/icons_rasterize.c index 5603d84022d..00dbdcfa1e5 100644 --- a/source/blender/blenkernel/intern/icons_rasterize.c +++ b/source/blender/blenkernel/intern/icons_rasterize.c @@ -76,7 +76,7 @@ ImBuf *BKE_icon_geom_rasterize(const struct Icon_Geom *geom, const uchar(*pos)[2] = geom->coords; const uint *col = (void *)geom->colors; - /* TODO(campbell): Currently rasterizes to fixed size, then scales. + /* TODO(@campbellbarton): Currently rasterizes to fixed size, then scales. * Should rasterize to double size for eg instead. */ const int rect_size[2] = {max_ii(256, (int)size_x * 2), max_ii(256, (int)size_y * 2)}; diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 43e732b428d..98c317c547b 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -784,7 +784,7 @@ IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed) if (create_if_needed) { id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty"); id->properties->type = IDP_GROUP; - /* NOTE(campbell): Don't overwrite the data's name and type + /* NOTE(@campbellbarton): Don't overwrite the data's name and type * some functions might need this if they * don't have a real ID, should be named elsewhere. */ // strcpy(id->name, "top_level_group"); diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 07ce4e46e9b..461a6f15ca1 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -91,7 +91,7 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK); } -static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id) +static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id, ID *UNUSED(owner_id_hint)) { return ((Key *)id)->from; } diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 758a317e415..05a00fb54fd 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -94,6 +94,7 @@ BLI_INLINE void lib_override_object_posemode_transfer(ID *id_dst, ID *id_src) /** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bmain, const ID *id, + const ID *owner_id_hint, const ID **r_owner_id) { if (r_owner_id != nullptr) { @@ -104,7 +105,8 @@ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bma if (id_type->owner_get != nullptr) { /* The #IDTypeInfo::owner_get callback should not modify the arguments, so casting away const * is okay. */ - const ID *owner_id = id_type->owner_get(const_cast<Main *>(bmain), const_cast<ID *>(id)); + const ID *owner_id = id_type->owner_get( + const_cast<Main *>(bmain), const_cast<ID *>(id), const_cast<ID *>(owner_id_hint)); if (r_owner_id != nullptr) { *r_owner_id = owner_id; } @@ -115,13 +117,17 @@ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bma return id->override_library; } -IDOverrideLibrary *BKE_lib_override_library_get(Main *bmain, ID *id, ID **r_owner_id) +IDOverrideLibrary *BKE_lib_override_library_get(Main *bmain, + ID *id, + ID *owner_id_hint, + ID **r_owner_id) { /* Reuse the implementation of the const access function, which does not change the arguments. * Add const explicitly to make it clear to the compiler to avoid just calling this function. */ return const_cast<IDOverrideLibrary *>( BKE_lib_override_library_get(const_cast<const Main *>(bmain), const_cast<const ID *>(id), + const_cast<const ID *>(owner_id_hint), const_cast<const ID **>(r_owner_id))); } @@ -319,7 +325,7 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) { if (ID_IS_OVERRIDE_LIBRARY(id)) { const ID *override_owner_id; - BKE_lib_override_library_get(bmain, id, &override_owner_id); + BKE_lib_override_library_get(bmain, id, nullptr, &override_owner_id); return (override_owner_id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) != 0; } @@ -1087,8 +1093,9 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData * } const Library *reference_lib = - BKE_lib_override_library_get(bmain, id_owner, nullptr)->reference->lib; - const ID *to_id_reference = BKE_lib_override_library_get(bmain, to_id, nullptr)->reference; + BKE_lib_override_library_get(bmain, id_owner, nullptr, nullptr)->reference->lib; + const ID *to_id_reference = + BKE_lib_override_library_get(bmain, to_id, nullptr, nullptr)->reference; if (to_id_reference->lib != reference_lib) { /* We do not override data-blocks from other libraries, nor do we process them. */ continue; @@ -1439,7 +1446,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); ID *id_owner; int best_level_placeholder = 0; - BKE_lib_override_library_get(bmain, id, &id_owner); + BKE_lib_override_library_get(bmain, id, nullptr, &id_owner); return lib_override_root_find(bmain, id_owner, curr_level + 1, &best_level_placeholder); } /* This way we won't process again that ID, should we encounter it again through another @@ -1478,7 +1485,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); ID *id_owner; int best_level_placeholder = 0; - BKE_lib_override_library_get(bmain, best_root_id_candidate, &id_owner); + BKE_lib_override_library_get(bmain, best_root_id_candidate, nullptr, &id_owner); best_root_id_candidate = lib_override_root_find( bmain, id_owner, curr_level + 1, &best_level_placeholder); } @@ -1795,7 +1802,8 @@ static bool lib_override_library_resync(Main *bmain, /* While this should not happen in typical cases (and won't be properly supported here), * user is free to do all kind of very bad things, including having different local * overrides of a same linked ID in a same hierarchy. */ - IDOverrideLibrary *id_override_library = BKE_lib_override_library_get(bmain, id, nullptr); + IDOverrideLibrary *id_override_library = BKE_lib_override_library_get( + bmain, id, nullptr, nullptr); if (id_override_library->hierarchy_root != id_root->override_library->hierarchy_root) { continue; @@ -2177,7 +2185,7 @@ static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id) if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->owner_get != nullptr) { - id = id_type->owner_get(bmain, id); + id = id_type->owner_get(bmain, id, nullptr); } BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index a869bf4c4b0..38d1a30592d 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -696,8 +696,8 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, bool has_valid_from_users = false; /* Preemptively consider this ID as unused. That way if there is a loop of dependency leading * back to it, it won't create a fake 'valid user' detection. - * NOTE: This can only only be done for a subset of IDs, some types are never 'indirectly - * unused', same for IDs with a fake user. */ + * NOTE: This can only be done for a subset of IDs, some types are never 'indirectly unused', + * same for IDs with a fake user. */ if ((id->flag & LIB_FAKEUSER) == 0 && !ELEM(GS(id->name), ID_SCE, ID_WM, ID_SCR, ID_WS, ID_LI)) { id->tag |= tag; } @@ -713,7 +713,7 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, /* Directly 'by-pass' to actual real ID owner. */ const IDTypeInfo *type_info_from = BKE_idtype_get_info_from_id(id_from); BLI_assert(type_info_from->owner_get != NULL); - id_from = type_info_from->owner_get(bmain, id_from); + id_from = type_info_from->owner_get(bmain, id_from, NULL); } lib_query_unused_ids_tag_recurse( diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index dd58c9cc4fe..9424b615031 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -122,7 +122,7 @@ void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath) /* Not essential but set `filepath_abs` is an absolute copy of value which * is more useful if its kept in sync. */ if (BLI_path_is_rel(lib->filepath_abs)) { - /* NOTE(campbell): the file may be unsaved, in this case, setting the + /* NOTE(@campbellbarton): the file may be unsaved, in this case, setting the * `filepath_abs` on an indirectly linked path is not allowed from the * outliner, and its not really supported but allow from here for now * since making local could cause this to be directly linked. diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index f899901b54e..248d292664a 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -852,7 +852,7 @@ void BKE_object_material_resize(Main *bmain, Object *ob, const short totcol, boo ob->mat = newmatar; ob->matbits = newmatbits; } - /* XXX(campbell): why not realloc on shrink? */ + /* XXX(@campbellbarton): why not realloc on shrink? */ ob->totcol = totcol; if (ob->totcol && ob->actcol == 0) { diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.cc index 2a1c940493c..084fea6abbd 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.cc @@ -11,12 +11,12 @@ * texture coordinates are patched within the displist */ -#include <ctype.h> -#include <float.h> -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <cctype> +#include <cfloat> +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -72,11 +72,11 @@ static void metaball_copy_data(Main *UNUSED(bmain), BLI_duplicatelist(&metaball_dst->elems, &metaball_src->elems); - metaball_dst->mat = MEM_dupallocN(metaball_src->mat); + metaball_dst->mat = static_cast<Material **>(MEM_dupallocN(metaball_src->mat)); - metaball_dst->editelems = NULL; - metaball_dst->lastelem = NULL; - metaball_dst->batch_cache = NULL; + metaball_dst->editelems = nullptr; + metaball_dst->lastelem = nullptr; + metaball_dst->batch_cache = nullptr; } static void metaball_free_data(ID *id) @@ -107,11 +107,11 @@ static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_add /* Clean up, important in undo case to reduce false detection of changed datablocks. */ BLI_listbase_clear(&mb->disp); - mb->editelems = NULL; + mb->editelems = nullptr; /* Must always be cleared (meta's don't have their own edit-data). */ mb->needs_flush_to_id = 0; - mb->lastelem = NULL; - mb->batch_cache = NULL; + mb->lastelem = nullptr; + mb->batch_cache = nullptr; /* write LibData */ BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); @@ -139,12 +139,12 @@ static void metaball_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_list(reader, &(mb->elems)); BLI_listbase_clear(&mb->disp); - mb->editelems = NULL; + mb->editelems = nullptr; /* Must always be cleared (meta's don't have their own edit-data). */ mb->needs_flush_to_id = 0; - // mb->edit_elems.first = mb->edit_elems.last = NULL; - mb->lastelem = NULL; - mb->batch_cache = NULL; + // mb->edit_elems.first = mb->edit_elems.last = nullptr; + mb->lastelem = nullptr; + mb->batch_cache = nullptr; } static void metaball_blend_read_lib(BlendLibReader *reader, ID *id) @@ -166,49 +166,46 @@ static void metaball_blend_read_expand(BlendExpander *expander, ID *id) } IDTypeInfo IDType_ID_MB = { - .id_code = ID_MB, - .id_filter = FILTER_ID_MB, - .main_listbase_index = INDEX_ID_MB, - .struct_size = sizeof(MetaBall), - .name = "Metaball", - .name_plural = "metaballs", - .translation_context = BLT_I18NCONTEXT_ID_METABALL, - .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, - .asset_type_info = NULL, - - .init_data = metaball_init_data, - .copy_data = metaball_copy_data, - .free_data = metaball_free_data, - .make_local = NULL, - .foreach_id = metaball_foreach_id, - .foreach_cache = NULL, - .foreach_path = NULL, - .owner_get = NULL, - - .blend_write = metaball_blend_write, - .blend_read_data = metaball_blend_read_data, - .blend_read_lib = metaball_blend_read_lib, - .blend_read_expand = metaball_blend_read_expand, - - .blend_read_undo_preserve = NULL, - - .lib_override_apply_post = NULL, + /* id_code */ ID_MB, + /* id_filter */ FILTER_ID_MB, + /* main_listbase_index */ INDEX_ID_MB, + /* struct_size */ sizeof(MetaBall), + /* name */ "Metaball", + /* name_plural */ "metaballs", + /* translation_context */ BLT_I18NCONTEXT_ID_METABALL, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, + /* asset_type_info */ nullptr, + + /* init_data */ metaball_init_data, + /* copy_data */ metaball_copy_data, + /* free_data */ metaball_free_data, + /* make_local */ nullptr, + /* foreach_id */ metaball_foreach_id, + /* foreach_cache */ nullptr, + /* foreach_path */ nullptr, + /* owner_get */ nullptr, + + /* blend_write */ metaball_blend_write, + /* blend_read_data */ metaball_blend_read_data, + /* blend_read_lib */ metaball_blend_read_lib, + /* blend_read_expand */ metaball_blend_read_expand, + + /* blend_read_undo_preserve */ nullptr, + + /* lib_override_apply_post */ nullptr, }; /* Functions */ MetaBall *BKE_mball_add(Main *bmain, const char *name) { - MetaBall *mb; - - mb = BKE_id_new(bmain, ID_MB, name); - + MetaBall *mb = static_cast<MetaBall *>(BKE_id_new(bmain, ID_MB, name)); return mb; } MetaElem *BKE_mball_element_add(MetaBall *mb, const int type) { - MetaElem *ml = MEM_callocN(sizeof(MetaElem), "metaelem"); + MetaElem *ml = MEM_cnew<MetaElem>(__func__); unit_qt(ml->quat); @@ -260,8 +257,8 @@ void BKE_mball_texspace_calc(Object *ob) int tot; bool do_it = false; - if (ob->runtime.bb == NULL) { - ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "mb boundbox"); + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = MEM_cnew<BoundBox>(__func__); } bb = ob->runtime.bb; @@ -270,7 +267,7 @@ void BKE_mball_texspace_calc(Object *ob) (min)[0] = (min)[1] = (min)[2] = 1.0e30f; (max)[0] = (max)[1] = (max)[2] = -1.0e30f; - dl = ob->runtime.curve_cache->disp.first; + dl = static_cast<DispList *>(ob->runtime.curve_cache->disp.first); while (dl) { tot = dl->nr; if (tot) { @@ -299,13 +296,13 @@ BoundBox *BKE_mball_boundbox_get(Object *ob) { BLI_assert(ob->type == OB_MBALL); - if (ob->runtime.bb != NULL && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { + if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { return ob->runtime.bb; } /* This should always only be called with evaluated objects, * but currently RNA is a problem here... */ - if (ob->runtime.curve_cache != NULL) { + if (ob->runtime.curve_cache != nullptr) { BKE_mball_texspace_calc(ob); } @@ -329,8 +326,8 @@ float *BKE_mball_make_orco(Object *ob, ListBase *dispbase) loc[2] = (bb->vec[0][2] + bb->vec[1][2]) / 2.0f; size[2] = bb->vec[1][2] - loc[2]; - dl = dispbase->first; - orcodata = MEM_mallocN(sizeof(float[3]) * dl->nr, "MballOrco"); + dl = static_cast<DispList *>(dispbase->first); + orcodata = static_cast<float *>(MEM_mallocN(sizeof(float[3]) * dl->nr, __func__)); data = dl->verts; orco = orcodata; @@ -393,7 +390,7 @@ bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2) bool BKE_mball_is_any_selected(const MetaBall *mb) { - for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) { + LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) { if (ml->flag & SELECT) { return true; } @@ -415,7 +412,7 @@ bool BKE_mball_is_any_selected_multi(Base **bases, int bases_len) bool BKE_mball_is_any_unselected(const MetaBall *mb) { - for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) { + LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) { if ((ml->flag & SELECT) == 0) { return true; } @@ -451,9 +448,10 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) * Solving this case would drastically increase the complexity of this code though, so don't * think it would be worth it. */ - for (Object *ob_src = bmain->objects.first; ob_src != NULL && !ID_IS_LINKED(ob_src);) { + for (Object *ob_src = static_cast<Object *>(bmain->objects.first); + ob_src != nullptr && !ID_IS_LINKED(ob_src);) { if (ob_src->data != metaball_src) { - ob_src = ob_src->id.next; + ob_src = static_cast<Object *>(ob_src->id.next); continue; } @@ -466,12 +464,13 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) * Using this, it is possible to process the whole set of meta-balls with a single loop on the * whole list of Objects, though additionally going backward on part of the list in some cases. */ - Object *ob_iter = NULL; + Object *ob_iter = nullptr; int obactive_nr, ob_nr; char obactive_name[MAX_ID_NAME], ob_name[MAX_ID_NAME]; BLI_split_name_num(obactive_name, &obactive_nr, ob_src->id.name + 2, '.'); - for (ob_iter = ob_src->id.prev; ob_iter != NULL; ob_iter = ob_iter->id.prev) { + for (ob_iter = static_cast<Object *>(ob_src->id.prev); ob_iter != nullptr; + ob_iter = static_cast<Object *>(ob_iter->id.prev)) { if (ob_iter->id.name[2] != obactive_name[0]) { break; } @@ -483,10 +482,11 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) break; } - mball_data_properties_copy(ob_iter->data, metaball_src); + mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src); } - for (ob_iter = ob_src->id.next; ob_iter != NULL; ob_iter = ob_iter->id.next) { + for (ob_iter = static_cast<Object *>(ob_src->id.next); ob_iter != nullptr; + ob_iter = static_cast<Object *>(ob_iter->id.next)) { if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) { break; } @@ -498,7 +498,7 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) break; } - mball_data_properties_copy(ob_iter->data, metaball_src); + mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src); } ob_src = ob_iter; @@ -556,7 +556,7 @@ bool BKE_mball_minmax_ex( copy_v3_v3(centroid, &ml->x); } - /* TODO(campbell): non circle shapes cubes etc, probably nobody notices. */ + /* TODO(@campbellbarton): non circle shapes cubes etc, probably nobody notices. */ for (int i = -1; i != 3; i += 2) { copy_v3_v3(vec, centroid); add_v3_fl(vec, scale_mb * i); @@ -682,7 +682,7 @@ bool BKE_mball_select_all_multi_ex(Base **bases, int bases_len) bool changed_multi = false; for (uint ob_index = 0; ob_index < bases_len; ob_index++) { Object *obedit = bases[ob_index]->object; - MetaBall *mb = obedit->data; + MetaBall *mb = static_cast<MetaBall *>(obedit->data); changed_multi |= BKE_mball_select_all(mb); } return changed_multi; @@ -705,7 +705,7 @@ bool BKE_mball_deselect_all_multi_ex(Base **bases, int bases_len) bool changed_multi = false; for (uint ob_index = 0; ob_index < bases_len; ob_index++) { Object *obedit = bases[ob_index]->object; - MetaBall *mb = obedit->data; + MetaBall *mb = static_cast<MetaBall *>(obedit->data); changed_multi |= BKE_mball_deselect_all(mb); DEG_id_tag_update(&mb->id, ID_RECALC_SELECT); } @@ -737,8 +737,8 @@ bool BKE_mball_select_swap_multi_ex(Base **bases, int bases_len) /* Draw Engine */ -void (*BKE_mball_batch_cache_dirty_tag_cb)(MetaBall *mb, int mode) = NULL; -void (*BKE_mball_batch_cache_free_cb)(MetaBall *mb) = NULL; +void (*BKE_mball_batch_cache_dirty_tag_cb)(MetaBall *mb, int mode) = nullptr; +void (*BKE_mball_batch_cache_free_cb)(MetaBall *mb) = nullptr; void BKE_mball_batch_cache_dirty_tag(MetaBall *mb, int mode) { diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 5775d29479e..c7a32fd7b03 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -39,6 +39,7 @@ #include "BLT_translation.h" #include "BKE_anim_data.h" +#include "BKE_attribute.hh" #include "BKE_bpath.h" #include "BKE_deform.h" #include "BKE_editmesh.h" @@ -65,8 +66,13 @@ #include "BLO_read_write.h" using blender::float3; +<<<<<<< HEAD using blender::IndexRange; using blender::MutableSpan; +======= +using blender::MutableSpan; +using blender::VArray; +>>>>>>> master using blender::Vector; static void mesh_clear_geometry(Mesh *mesh); @@ -246,10 +252,14 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address memset(&mesh->pdata, 0, sizeof(mesh->pdata)); } else { - CustomData_blend_write_prepare(mesh->vdata, vert_layers); - CustomData_blend_write_prepare(mesh->edata, edge_layers); + if (!BLO_write_is_undo(writer)) { + BKE_mesh_legacy_convert_hide_layers_to_flags(mesh); + } + + CustomData_blend_write_prepare(mesh->vdata, vert_layers, {".hide_vert"}); + CustomData_blend_write_prepare(mesh->edata, edge_layers, {".hide_edge"}); CustomData_blend_write_prepare(mesh->ldata, loop_layers); - CustomData_blend_write_prepare(mesh->pdata, poly_layers); + CustomData_blend_write_prepare(mesh->pdata, poly_layers, {".hide_poly"}); } if (!BLO_write_is_undo(writer)) { @@ -333,7 +343,11 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) } if (!BLO_read_data_is_undo(reader)) { +<<<<<<< HEAD BKE_mesh_legacy_bevel_weight_to_layers(mesh); +======= + BKE_mesh_legacy_convert_flags_to_hide_layers(mesh); +>>>>>>> master } /* We don't expect to load normals from files, since they are derived data. */ @@ -773,10 +787,10 @@ static void mesh_ensure_tessellation_customdata(Mesh *me) /* TODO: add some `--debug-mesh` option. */ if (G.debug & G_DEBUG) { - /* NOTE(campbell): this warning may be un-called for if we are initializing the mesh for - * the first time from #BMesh, rather than giving a warning about this we could be smarter - * and check if there was any data to begin with, for now just print the warning with - * some info to help troubleshoot what's going on. */ + /* NOTE(@campbellbarton): this warning may be un-called for if we are initializing the mesh + * for the first time from #BMesh, rather than giving a warning about this we could be + * smarter and check if there was any data to begin with, for now just print the warning + * with some info to help troubleshoot what's going on. */ printf( "%s: warning! Tessellation uvs or vcol data got out of sync, " "had to reset!\n CD_MTFACE: %d != CD_MLOOPUV: %d || CD_MCOL: %d != " diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index fce001826e0..000f356fd6a 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -22,6 +22,7 @@ #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -54,6 +55,8 @@ #include "DEG_depsgraph_query.h" using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; /* Define for cases when you want extra validation of mesh * after certain modifications. @@ -127,29 +130,28 @@ void BKE_mesh_from_metaball(ListBase *lb, Mesh *me) /** * Specialized function to use when we _know_ existing edges don't overlap with poly edges. */ -static void make_edges_mdata_extend( - MEdge **r_alledge, int *r_totedge, const MPoly *mpoly, MLoop *mloop, const int totpoly) +static void make_edges_mdata_extend(Mesh &mesh) { - int totedge = *r_totedge; - int totedge_new; - EdgeHash *eh; - uint eh_reserve; + int totedge = mesh.totedge; const MPoly *mp; int i; - eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(totpoly)); - eh = BLI_edgehash_new_ex(__func__, eh_reserve); + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + MutableSpan<MLoop> loops(mesh.mloop, mesh.totloop); - for (i = 0, mp = mpoly; i < totpoly; i++, mp++) { - BKE_mesh_poly_edgehash_insert(eh, mp, mloop + mp->loopstart); + const int eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(mesh.totpoly)); + EdgeHash *eh = BLI_edgehash_new_ex(__func__, eh_reserve); + + for (const MPoly &poly : polys) { + BKE_mesh_poly_edgehash_insert(eh, &poly, &loops[poly.loopstart]); } - totedge_new = BLI_edgehash_len(eh); + const int totedge_new = BLI_edgehash_len(eh); #ifdef DEBUG /* ensure that there's no overlap! */ if (totedge_new) { - MEdge *medge = *r_alledge; + MEdge *medge = mesh.medge; for (i = 0; i < totedge; i++, medge++) { BLI_assert(BLI_edgehash_haskey(eh, medge->v1, medge->v2) == false); } @@ -157,19 +159,15 @@ static void make_edges_mdata_extend( #endif if (totedge_new) { - EdgeHashIterator *ehi; - MEdge *medge; - uint e_index = totedge; + CustomData_realloc(&mesh.edata, totedge + totedge_new); + BKE_mesh_update_customdata_pointers(&mesh, false); - *r_alledge = medge = (MEdge *)(*r_alledge ? - MEM_reallocN(*r_alledge, - sizeof(MEdge) * (totedge + totedge_new)) : - MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__)); - medge += totedge; + MEdge *medge = mesh.medge + totedge; - totedge += totedge_new; + mesh.totedge += totedge_new; - /* --- */ + EdgeHashIterator *ehi; + uint e_index = totedge; for (ehi = BLI_edgehashIterator_new(eh); BLI_edgehashIterator_isDone(ehi) == false; BLI_edgehashIterator_step(ehi), ++medge, e_index++) { BLI_edgehashIterator_getKey(ehi, &medge->v1, &medge->v2); @@ -180,10 +178,8 @@ static void make_edges_mdata_extend( } BLI_edgehashIterator_free(ehi); - *r_totedge = totedge; - - for (i = 0, mp = mpoly; i < totpoly; i++, mp++) { - MLoop *l = &mloop[mp->loopstart]; + for (i = 0, mp = mesh.mpoly; i < mesh.totpoly; i++, mp++) { + MLoop *l = &loops[mp->loopstart]; MLoop *l_prev = (l + (mp->totloop - 1)); int j; for (j = 0; j < mp->totloop; j++, l++) { @@ -197,25 +193,8 @@ static void make_edges_mdata_extend( BLI_edgehash_free(eh, nullptr); } -/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */ -/* use specified dispbase */ -static int mesh_nurbs_displist_to_mdata(const Curve *cu, - const ListBase *dispbase, - MVert **r_allvert, - int *r_totvert, - MEdge **r_alledge, - int *r_totedge, - MLoop **r_allloop, - MPoly **r_allpoly, - MLoopUV **r_alluv, - int *r_totloop, - int *r_totpoly) +static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispbase) { - MVert *mvert; - MPoly *mpoly; - MLoop *mloop; - MLoopUV *mloopuv = nullptr; - MEdge *medge; const float *data; int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0; int p1, p2, p3, p4, *index; @@ -257,21 +236,21 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, } if (totvert == 0) { - /* Make Sure you check ob->data is a curve. */ - // error("can't convert"); - return -1; + return BKE_mesh_new_nomain(0, 0, 0, 0, 0); } - *r_allvert = mvert = (MVert *)MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert"); - *r_alledge = medge = (MEdge *)MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge"); - *r_allloop = mloop = (MLoop *)MEM_calloc_arrayN( - totpoly, sizeof(MLoop[4]), "nurbs_init mloop"); /* totloop */ - *r_allpoly = mpoly = (MPoly *)MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop"); + Mesh *mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly); + MutableSpan<MVert> verts(mesh->mvert, mesh->totvert); + MutableSpan<MEdge> edges(mesh->medge, mesh->totedge); + MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly); + MutableSpan<MLoop> loops(mesh->mloop, mesh->totloop); - if (r_alluv) { - *r_alluv = mloopuv = (MLoopUV *)MEM_calloc_arrayN( - totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv"); - } + MVert *mvert = verts.data(); + MEdge *medge = edges.data(); + MPoly *mpoly = polys.data(); + MLoop *mloop = loops.data(); + MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_add_layer_named( + &mesh->ldata, CD_MLOOPUV, CD_CALLOC, nullptr, mesh->totloop, "UVMap")); /* verts and faces */ vertcount = 0; @@ -346,7 +325,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, mloop[0].v = startvert + index[0]; mloop[1].v = startvert + index[2]; mloop[2].v = startvert + index[1]; - mpoly->loopstart = (int)(mloop - (*r_allloop)); + mpoly->loopstart = (int)(mloop - loops.data()); mpoly->totloop = 3; mpoly->mat_nr = dl->col; @@ -406,7 +385,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, mloop[1].v = p3; mloop[2].v = p4; mloop[3].v = p2; - mpoly->loopstart = (int)(mloop - (*r_allloop)); + mpoly->loopstart = (int)(mloop - loops.data()); mpoly->totloop = 4; mpoly->mat_nr = dl->col; @@ -458,15 +437,10 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, } if (totpoly) { - make_edges_mdata_extend(r_alledge, &totedge, *r_allpoly, *r_allloop, totpoly); + make_edges_mdata_extend(*mesh); } - *r_totpoly = totpoly; - *r_totloop = totloop; - *r_totedge = totedge; - *r_totvert = totvert; - - return 0; + return mesh; } /** @@ -487,60 +461,12 @@ static void mesh_copy_texture_space_from_curve_type(const Curve *cu, Mesh *me) Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *dispbase) { const Curve *cu = (const Curve *)ob->data; - Mesh *mesh; - MVert *allvert; - MEdge *alledge; - MLoop *allloop; - MPoly *allpoly; - MLoopUV *alluv = nullptr; - int totvert, totedge, totloop, totpoly; - - if (mesh_nurbs_displist_to_mdata(cu, - dispbase, - &allvert, - &totvert, - &alledge, - &totedge, - &allloop, - &allpoly, - &alluv, - &totloop, - &totpoly) != 0) { - /* Error initializing mdata. This often happens when curve is empty */ - return BKE_mesh_new_nomain(0, 0, 0, 0, 0); - } - - mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly); - - if (totvert != 0) { - memcpy(mesh->mvert, allvert, totvert * sizeof(MVert)); - } - if (totedge != 0) { - memcpy(mesh->medge, alledge, totedge * sizeof(MEdge)); - } - if (totloop != 0) { - memcpy(mesh->mloop, allloop, totloop * sizeof(MLoop)); - } - if (totpoly != 0) { - memcpy(mesh->mpoly, allpoly, totpoly * sizeof(MPoly)); - } - - if (alluv) { - const char *uvname = "UVMap"; - CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, totloop, uvname); - } + Mesh *mesh = mesh_nurbs_displist_to_mesh(cu, dispbase); mesh_copy_texture_space_from_curve_type(cu, mesh); - - /* Copy curve materials. */ mesh->mat = (Material **)MEM_dupallocN(cu->mat); mesh->totcol = cu->totcol; - MEM_freeN(allvert); - MEM_freeN(alledge); - MEM_freeN(allloop); - MEM_freeN(allpoly); - return mesh; } @@ -985,6 +911,12 @@ static Mesh *mesh_new_from_curve_type_object(const Object *object) /* If evaluating the curve replaced object data with different data, free the original data. */ if (temp_data != temp_object->data) { + if (GS(temp_data->name) == ID_CU_LEGACY) { + /* Clear edit mode pointers that were explicitly copied to the temporary curve. */ + Curve *curve = reinterpret_cast<Curve *>(temp_data); + curve->editfont = nullptr; + curve->editnurb = nullptr; + } BKE_id_free(nullptr, temp_data); } diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index 7d26262a504..9dba8eab340 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -22,15 +22,17 @@ #include "BLI_math.h" #include "BLI_span.hh" #include "BLI_utildefines.h" +#include "BLI_virtual_array.hh" #include "BKE_customdata.h" +#include "BKE_attribute.hh" #include "BKE_mesh.h" #include "BKE_multires.h" -using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::VArray; /* -------------------------------------------------------------------- */ /** \name Polygon Calculations @@ -732,75 +734,90 @@ void BKE_mesh_polygons_flip(MPoly *mpoly, MLoop *mloop, CustomData *ldata, int t /** \name Mesh Flag Flushing * \{ */ -void BKE_mesh_flush_hidden_from_verts_ex(const MVert *mvert, - const MLoop *mloop, - MEdge *medge, - const int totedge, - MPoly *mpoly, - const int totpoly) +void BKE_mesh_flush_hidden_from_verts(Mesh *me) { - int i, j; + using namespace blender; + using namespace blender::bke; + MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); - for (i = 0; i < totedge; i++) { - MEdge *e = &medge[i]; - if (mvert[e->v1].flag & ME_HIDE || mvert[e->v2].flag & ME_HIDE) { - e->flag |= ME_HIDE; - } - else { - e->flag &= ~ME_HIDE; - } + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + if (hide_vert.is_single() && !hide_vert.get_internal_single()) { + attributes.remove(".hide_edge"); + attributes.remove(".hide_poly"); + return; } - for (i = 0; i < totpoly; i++) { - MPoly *p = &mpoly[i]; - p->flag &= (char)~ME_HIDE; - for (j = 0; j < p->totloop; j++) { - if (mvert[mloop[p->loopstart + j].v].flag & ME_HIDE) { - p->flag |= ME_HIDE; - } - } + const VArraySpan<bool> hide_vert_span{hide_vert}; + const Span<MEdge> edges(me->medge, me->totedge); + const Span<MPoly> polys(me->mpoly, me->totpoly); + const Span<MLoop> loops(me->mloop, me->totloop); + + /* Hide edges when either of their vertices are hidden. */ + SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_edge", ATTR_DOMAIN_EDGE); + for (const int i : edges.index_range()) { + const MEdge &edge = edges[i]; + hide_edge.span[i] = hide_vert_span[edge.v1] || hide_vert_span[edge.v2]; } -} -void BKE_mesh_flush_hidden_from_verts(Mesh *me) -{ - BKE_mesh_flush_hidden_from_verts_ex( - me->mvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly); + hide_edge.finish(); + + /* Hide polygons when any of their vertices are hidden. */ + SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_poly", ATTR_DOMAIN_FACE); + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + hide_poly.span[i] = std::any_of(poly_loops.begin(), poly_loops.end(), [&](const MLoop &loop) { + return hide_vert_span[loop.v]; + }); + } + hide_poly.finish(); } -void BKE_mesh_flush_hidden_from_polys_ex(MVert *mvert, - const MLoop *mloop, - MEdge *medge, - const int UNUSED(totedge), - const MPoly *mpoly, - const int totpoly) +void BKE_mesh_flush_hidden_from_polys(Mesh *me) { - int i = totpoly; - for (const MPoly *mp = mpoly; i--; mp++) { - if (mp->flag & ME_HIDE) { - const MLoop *ml; - int j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - mvert[ml->v].flag |= ME_HIDE; - medge[ml->e].flag |= ME_HIDE; + using namespace blender; + using namespace blender::bke; + MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); + + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (hide_poly.is_single() && !hide_poly.get_internal_single()) { + attributes.remove(".hide_vert"); + attributes.remove(".hide_edge"); + return; + } + const VArraySpan<bool> hide_poly_span{hide_poly}; + const Span<MPoly> polys(me->mpoly, me->totpoly); + const Span<MLoop> loops(me->mloop, me->totloop); + SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_vert", ATTR_DOMAIN_POINT); + SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_edge", ATTR_DOMAIN_EDGE); + + /* Hide all edges or vertices connected to hidden polygons. */ + for (const int i : polys.index_range()) { + if (hide_poly_span[i]) { + const MPoly &poly = polys[i]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + hide_vert.span[loop.v] = true; + hide_edge.span[loop.e] = true; } } } - - i = totpoly; - for (const MPoly *mp = mpoly; i--; mp++) { - if ((mp->flag & ME_HIDE) == 0) { - const MLoop *ml; - int j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - mvert[ml->v].flag &= (char)~ME_HIDE; - medge[ml->e].flag &= (short)~ME_HIDE; + /* Unhide vertices and edges connected to visible polygons. */ + for (const int i : polys.index_range()) { + if (!hide_poly_span[i]) { + const MPoly &poly = polys[i]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + hide_vert.span[loop.v] = false; + hide_edge.span[loop.e] = false; } } } -} -void BKE_mesh_flush_hidden_from_polys(Mesh *me) -{ - BKE_mesh_flush_hidden_from_polys_ex( - me->mvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly); + + hide_vert.finish(); + hide_edge.finish(); } void BKE_mesh_flush_select_from_polys_ex(MVert *mvert, @@ -848,11 +865,13 @@ void BKE_mesh_flush_select_from_polys(Mesh *me) static void mesh_flush_select_from_verts(const Span<MVert> verts, const Span<MLoop> loops, + const VArray<bool> &hide_edge, + const VArray<bool> &hide_poly, MutableSpan<MEdge> edges, MutableSpan<MPoly> polys) { for (const int i : edges.index_range()) { - if ((edges[i].flag & ME_HIDE) == 0) { + if (!hide_edge[i]) { MEdge &edge = edges[i]; if ((verts[edge.v1].flag & SELECT) && (verts[edge.v2].flag & SELECT)) { edge.flag |= SELECT; @@ -864,7 +883,7 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts, } for (const int i : polys.index_range()) { - if (polys[i].flag & ME_HIDE) { + if (hide_poly[i]) { continue; } MPoly &poly = polys[i]; @@ -885,10 +904,14 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts, void BKE_mesh_flush_select_from_verts(Mesh *me) { - mesh_flush_select_from_verts({me->mvert, me->totvert}, - {me->mloop, me->totloop}, - {me->medge, me->totedge}, - {me->mpoly, me->totpoly}); + const blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*me); + mesh_flush_select_from_verts( + {me->mvert, me->totvert}, + {me->mloop, me->totloop}, + attributes.lookup_or_default<bool>(".hide_edge", ATTR_DOMAIN_EDGE, false), + attributes.lookup_or_default<bool>(".hide_poly", ATTR_DOMAIN_FACE, false), + {me->medge, me->totedge}, + {me->mpoly, me->totpoly}); } /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 16fd9cf9de9..00c6e3f2806 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -18,8 +18,10 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_polyfill_2d.h" +#include "BLI_task.hh" #include "BLI_utildefines.h" +#include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_mesh_legacy_convert.h" @@ -876,6 +878,7 @@ void BKE_mesh_add_mface_layers(CustomData *fdata, CustomData *ldata, int total) /** \} */ /* -------------------------------------------------------------------- */ +<<<<<<< HEAD /** \name Bevel Weight Conversion * \{ */ @@ -929,6 +932,88 @@ void BKE_mesh_legacy_bevel_weight_to_layers(Mesh *mesh) for (const int i : edges.index_range()) { weights[i] = edges[i].bweight / 255.0f; } +======= +/** \name Hide Attribute and Legacy Flag Conversion + * \{ */ + +void BKE_mesh_legacy_convert_hide_layers_to_flags(Mesh *mesh) +{ + using namespace blender; + using namespace blender::bke; + const AttributeAccessor attributes = mesh_attributes(*mesh); + + MutableSpan<MVert> verts(mesh->mvert, mesh->totvert); + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(verts[i].flag, hide_vert[i], ME_HIDE); + } + }); + + MutableSpan<MEdge> edges(mesh->medge, mesh->totedge); + const VArray<bool> hide_edge = attributes.lookup_or_default<bool>( + ".hide_edge", ATTR_DOMAIN_EDGE, false); + threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(edges[i].flag, hide_edge[i], ME_HIDE); + } + }); + + MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(polys[i].flag, hide_poly[i], ME_HIDE); + } + }); +} + +void BKE_mesh_legacy_convert_flags_to_hide_layers(Mesh *mesh) +{ + using namespace blender; + using namespace blender::bke; + MutableAttributeAccessor attributes = mesh_attributes_for_write(*mesh); + + const Span<MVert> verts(mesh->mvert, mesh->totvert); + if (std::any_of( + verts.begin(), verts.end(), [](const MVert &vert) { return vert.flag & ME_HIDE; })) { + SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_vert", ATTR_DOMAIN_POINT); + threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + hide_vert.span[i] = verts[i].flag & ME_HIDE; + } + }); + hide_vert.finish(); + } + + const Span<MEdge> edges(mesh->medge, mesh->totedge); + if (std::any_of( + edges.begin(), edges.end(), [](const MEdge &edge) { return edge.flag & ME_HIDE; })) { + SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_edge", ATTR_DOMAIN_EDGE); + threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + hide_edge.span[i] = edges[i].flag & ME_HIDE; + } + }); + hide_edge.finish(); + } + + const Span<MPoly> polys(mesh->mpoly, mesh->totpoly); + if (std::any_of( + polys.begin(), polys.end(), [](const MPoly &poly) { return poly.flag & ME_HIDE; })) { + SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_poly", ATTR_DOMAIN_FACE); + threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + hide_poly.span[i] = polys[i].flag & ME_HIDE; + } + }); + hide_poly.finish(); +>>>>>>> master } } diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index 9c4098e2db6..798fe087ea8 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -29,6 +29,7 @@ /* ngon version wip, based on BM_uv_vert_map_create */ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, + const bool *hide_poly, const MLoop *mloop, const MLoopUV *mloopuv, uint totpoly, @@ -51,7 +52,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, /* generate UvMapVert array */ mp = mpoly; for (a = 0; a < totpoly; a++, mp++) { - if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) { + if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) { totuv += mp->totloop; } } @@ -74,7 +75,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, mp = mpoly; for (a = 0; a < totpoly; a++, mp++) { - if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) { + if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) { float(*tf_uv)[2] = NULL; if (use_winding) { diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c index a677a0d6ebb..1772419e1f3 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ b/source/blender/blenkernel/intern/mesh_tangent.c @@ -716,7 +716,7 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval, { BKE_mesh_runtime_looptri_ensure(me_eval); - /* TODO(campbell): store in Mesh.runtime to avoid recalculation. */ + /* TODO(@campbellbarton): store in Mesh.runtime to avoid recalculation. */ short tangent_mask = 0; BKE_mesh_calc_loop_tangent_ex(me_eval->mvert, me_eval->mpoly, diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 01eb4970f7e..60d6b51594a 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -142,7 +142,8 @@ static ModifierData *modifier_allocate_and_init(int type) md->type = type; md->mode = eModifierMode_Realtime | eModifierMode_Render; md->flag = eModifierFlag_OverrideLibrary_Local; - md->ui_expand_flag = 1; /* Only open the main panel at the beginning, not the sub-panels. */ + /* Only open the main panel at the beginning, not the sub-panels. */ + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; if (mti->flags & eModifierTypeFlag_EnableInEditmode) { md->mode |= eModifierMode_Editmode; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index ba473b29474..d50b8662f82 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -404,7 +404,7 @@ static void node_foreach_path(ID *id, BPathForeachPathData *bpath_data) } } -static ID *node_owner_get(Main *bmain, ID *id) +static ID *node_owner_get(Main *bmain, ID *id, ID *owner_id_hint) { if ((id->flag & LIB_EMBEDDED_DATA) == 0) { return id; @@ -412,6 +412,12 @@ static ID *node_owner_get(Main *bmain, ID *id) /* TODO: Sort this NO_MAIN or not for embedded node trees. See T86119. */ // BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0); + bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id); + + if (owner_id_hint != nullptr && ntreeFromID(owner_id_hint) == ntree) { + return owner_id_hint; + } + ListBase *lists[] = {&bmain->materials, &bmain->lights, &bmain->worlds, @@ -421,7 +427,6 @@ static ID *node_owner_get(Main *bmain, ID *id) &bmain->simulations, nullptr}; - bNodeTree *ntree = (bNodeTree *)id; for (int i = 0; lists[i] != nullptr; i++) { LISTBASE_FOREACH (ID *, id_iter, lists[i]) { if (ntreeFromID(id_iter) == ntree) { @@ -3047,7 +3052,9 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) } } - if (node_has_id) { + /* Also update relations for the scene time node, which causes a dependency + * on time that users expect to be removed when the node is removed. */ + if (node_has_id || node->type == GEO_NODE_INPUT_SCENE_TIME) { if (bmain != nullptr) { DEG_relations_tag_update(bmain); } diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 407a2c8955c..cc3a8b5bb0e 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -29,6 +29,7 @@ #include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_vfont_types.h" +#include "DNA_volume_types.h" #include "BKE_collection.h" #include "BKE_duplilist.h" @@ -164,10 +165,8 @@ static bool copy_dupli_context( * * \param mat: is transform of the object relative to current context (including #Object.obmat). */ -static DupliObject *make_dupli(const DupliContext *ctx, - Object *ob, - const float mat[4][4], - int index) +static DupliObject *make_dupli( + const DupliContext *ctx, Object *ob, const ID *object_data, const float mat[4][4], int index) { DupliObject *dob; int i; @@ -182,7 +181,7 @@ static DupliObject *make_dupli(const DupliContext *ctx, } dob->ob = ob; - dob->ob_data = (ID *)ob->data; + dob->ob_data = const_cast<ID *>(object_data); mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat); dob->type = ctx->gen->type; @@ -226,6 +225,14 @@ static DupliObject *make_dupli(const DupliContext *ctx, return dob; } +static DupliObject *make_dupli(const DupliContext *ctx, + Object *ob, + const float mat[4][4], + int index) +{ + return make_dupli(ctx, ob, static_cast<ID *>(ob->data), mat, index); +} + /** * Recursive dupli-objects. * @@ -777,28 +784,24 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, int component_index = 0; if (ctx->object->type != OB_MESH || geometry_set_is_instance) { if (const Mesh *mesh = geometry_set.get_mesh_for_read()) { - DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); - dupli->ob_data = (ID *)mesh; + make_dupli(ctx, ctx->object, &mesh->id, parent_transform, component_index++); } } if (ctx->object->type != OB_VOLUME || geometry_set_is_instance) { if (const Volume *volume = geometry_set.get_volume_for_read()) { - DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); - dupli->ob_data = (ID *)volume; + make_dupli(ctx, ctx->object, &volume->id, parent_transform, component_index++); } } if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT, OB_CURVES) || geometry_set_is_instance) { if (const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>()) { if (const Curve *curve = component->get_curve_for_render()) { - DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); - dupli->ob_data = (ID *)curve; + make_dupli(ctx, ctx->object, &curve->id, parent_transform, component_index++); } } } if (ctx->object->type != OB_POINTCLOUD || geometry_set_is_instance) { if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) { - DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); - dupli->ob_data = (ID *)pointcloud; + make_dupli(ctx, ctx->object, &pointcloud->id, parent_transform, component_index++); } } const bool creates_duplis_for_components = component_index >= 1; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 1f7df2773dc..99c4d92d284 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -281,45 +281,39 @@ void BKE_object_eval_uber_transform(Depsgraph *UNUSED(depsgraph), Object *UNUSED { } -void BKE_object_data_batch_cache_dirty_tag(ID *object_data) +void BKE_object_batch_cache_dirty_tag(Object *ob) { - switch (GS(object_data->name)) { - case ID_ME: - BKE_mesh_batch_cache_dirty_tag((struct Mesh *)object_data, BKE_MESH_BATCH_DIRTY_ALL); + switch (ob->type) { + case OB_MESH: + BKE_mesh_batch_cache_dirty_tag((struct Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); break; - case ID_LT: - BKE_lattice_batch_cache_dirty_tag((struct Lattice *)object_data, - BKE_LATTICE_BATCH_DIRTY_ALL); + case OB_LATTICE: + BKE_lattice_batch_cache_dirty_tag((struct Lattice *)ob->data, BKE_LATTICE_BATCH_DIRTY_ALL); break; - case ID_CU_LEGACY: - BKE_curve_batch_cache_dirty_tag((struct Curve *)object_data, BKE_CURVE_BATCH_DIRTY_ALL); + case OB_CURVES_LEGACY: + BKE_curve_batch_cache_dirty_tag((struct Curve *)ob->data, BKE_CURVE_BATCH_DIRTY_ALL); break; - case ID_MB: - BKE_mball_batch_cache_dirty_tag((struct MetaBall *)object_data, BKE_MBALL_BATCH_DIRTY_ALL); + case OB_MBALL: + BKE_mball_batch_cache_dirty_tag((struct MetaBall *)ob->data, BKE_MBALL_BATCH_DIRTY_ALL); break; - case ID_GD: - BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)object_data); + case OB_GPENCIL: + BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)ob->data); break; - case ID_CV: - BKE_curves_batch_cache_dirty_tag((struct Curves *)object_data, BKE_CURVES_BATCH_DIRTY_ALL); + case OB_CURVES: + BKE_curves_batch_cache_dirty_tag((struct Curves *)ob->data, BKE_CURVES_BATCH_DIRTY_ALL); break; - case ID_PT: - BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)object_data, + case OB_POINTCLOUD: + BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)ob->data, BKE_POINTCLOUD_BATCH_DIRTY_ALL); break; - case ID_VO: - BKE_volume_batch_cache_dirty_tag((struct Volume *)object_data, BKE_VOLUME_BATCH_DIRTY_ALL); + case OB_VOLUME: + BKE_volume_batch_cache_dirty_tag((struct Volume *)ob->data, BKE_VOLUME_BATCH_DIRTY_ALL); break; default: break; } } -void BKE_object_batch_cache_dirty_tag(Object *ob) -{ - BKE_object_data_batch_cache_dirty_tag(ob->data); -} - void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob) { DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob); diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index dec9a594938..cd1f24fee37 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -1385,9 +1385,8 @@ void BKE_ocean_bake(struct Ocean *o, void (*update_cb)(void *, float progress, int *cancel), void *update_cb_data) { - /* NOTE(campbell): some of these values remain uninitialized unless certain options - * are enabled, take care that BKE_ocean_eval_ij() initializes a member - * before use. */ + /* NOTE(@campbellbarton): some of these values remain uninitialized unless certain options + * are enabled, take care that #BKE_ocean_eval_ij() initializes a member before use. */ OceanResult ocr; ImageFormatData imf = {0}; @@ -1441,7 +1440,7 @@ void BKE_ocean_bake(struct Ocean *o, rgb_to_rgba_unit_alpha(&ibuf_disp->rect_float[4 * (res_x * y + x)], ocr.disp); if (o->_do_jacobian) { - /* TODO(campbell): cleanup unused code. */ + /* TODO(@campbellbarton): cleanup unused code. */ float /* r, */ /* UNUSED */ pr = 0.0f, foam_result; float neg_disp, neg_eplus; diff --git a/source/blender/blenkernel/intern/outliner_treehash.c b/source/blender/blenkernel/intern/outliner_treehash.c index 03c327bec2f..09e2baf2be1 100644 --- a/source/blender/blenkernel/intern/outliner_treehash.c +++ b/source/blender/blenkernel/intern/outliner_treehash.c @@ -21,11 +21,20 @@ typedef struct TseGroup { TreeStoreElem **elems; + /* Index of last used #TreeStoreElem item, to speed up search for another one. */ int lastused; + /* Counter used to reduce the amount of 'rests' of `lastused` index, otherwise search for unused + * item is exponential and becomes critically slow when there are a lot of items in the group. */ + int lastused_reset_count; + /* Number of items currently in use. */ int size; + /* Number of items currently allocated. */ int allocated; } TseGroup; +/* Only allow reset of #TseGroup.lastused counter to 0 once every 1k search. */ +#define TSEGROUP_LASTUSED_RESET_VALUE 10000 + /* Allocate structure for TreeStoreElements; * Most of elements in treestore have no duplicates, * so there is no need to preallocate memory for more than one pointer */ @@ -47,6 +56,7 @@ static void tse_group_add_element(TseGroup *tse_group, TreeStoreElem *elem) sizeof(TreeStoreElem *) * tse_group->allocated); } tse_group->elems[tse_group->size] = elem; + tse_group->lastused = tse_group->size; tse_group->size++; } @@ -136,6 +146,7 @@ void BKE_outliner_treehash_clear_used(void *treehash) GHASH_ITER (gh_iter, treehash) { TseGroup *group = BLI_ghashIterator_getValue(&gh_iter); group->lastused = 0; + group->lastused_reset_count = 0; } } @@ -157,7 +168,6 @@ void BKE_outliner_treehash_add_element(void *treehash, TreeStoreElem *elem) *val_p = tse_group_create(); } group = *val_p; - group->lastused = 0; tse_group_add_element(group, elem); } @@ -204,7 +214,15 @@ TreeStoreElem *BKE_outliner_treehash_lookup_unused(void *treehash, int offset = group->lastused; for (int i = 0; i < size; i++, offset++) { + /* Once at the end of the array of items, in most cases it just means that all items are + * used, so only check the whole array once every TSEGROUP_LASTUSED_RESET_VALUE times. */ if (offset >= size) { + if (LIKELY(group->lastused_reset_count <= TSEGROUP_LASTUSED_RESET_VALUE)) { + group->lastused_reset_count++; + group->lastused = group->size - 1; + break; + } + group->lastused_reset_count = 0; offset = 0; } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index b540357ce49..922ea45703d 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1243,11 +1243,13 @@ void BKE_paint_blend_read_lib(BlendLibReader *reader, Scene *sce, Paint *p) } } -bool paint_is_face_hidden(const MLoopTri *lt, const MVert *mvert, const MLoop *mloop) +bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_vert, const MLoop *mloop) { - return ((mvert[mloop[lt->tri[0]].v].flag & ME_HIDE) || - (mvert[mloop[lt->tri[1]].v].flag & ME_HIDE) || - (mvert[mloop[lt->tri[2]].v].flag & ME_HIDE)); + if (!hide_vert) { + return false; + } + return ((hide_vert[mloop[lt->tri[0]].v]) || (hide_vert[mloop[lt->tri[1]].v]) || + (hide_vert[mloop[lt->tri[2]].v])); } bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y) @@ -2068,9 +2070,11 @@ void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh) } int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly"); for (int i = 0; i < mesh->totpoly; i++) { - if (!(mesh->mpoly[i].flag & ME_HIDE)) { + if (!(hide_poly && hide_poly[i])) { continue; } @@ -2095,9 +2099,13 @@ void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) return; } + bool *hide_poly = (bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"); + if (!hide_poly) { + return; + } + for (int i = 0; i < mesh->totpoly; i++) { - const bool is_face_set_visible = face_sets[i] >= 0; - SET_FLAG_FROM_TEST(mesh->mpoly[i].flag, !is_face_set_visible, ME_HIDE); + hide_poly[i] = face_sets[i] < 0; } BKE_mesh_flush_hidden_from_polys(mesh); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 2471d3baa59..85a8d6c817f 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -5413,8 +5413,8 @@ void BKE_particle_system_blend_read_lib(BlendLibReader *reader, BLO_read_id_address(reader, id->lib, &psys->target_ob); if (psys->clmd) { - /* XXX(campbell): from reading existing code this seems correct but intended usage of - * pointcache /w cloth should be added in 'ParticleSystem'. */ + /* XXX(@campbellbarton): from reading existing code this seems correct but intended usage + * of pointcache /w cloth should be added in 'ParticleSystem'. */ psys->clmd->point_cache = psys->pointcache; psys->clmd->ptcaches.first = psys->clmd->ptcaches.last = NULL; BLO_read_id_address(reader, id->lib, &psys->clmd->coll_parms->group); diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 4a8f029beee..e9bbcea241e 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -969,7 +969,7 @@ void psys_get_birth_coords( float tmat[3][3]; /* NOTE: utan_local is not taken from 'utan', we calculate from rot_vec/vtan. */ - /* NOTE(campbell): it looks like rotation phase may be applied twice + /* NOTE(@campbellbarton): it looks like rotation phase may be applied twice * (once with vtan, again below) however this isn't the case. */ float *rot_vec_local = tmat[0]; float *vtan_local = tmat[1]; diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index dae9788d21c..4e6418942be 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -287,7 +287,7 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node) } if (has_visible == false) { - if (!paint_is_face_hidden(lt, pbvh->verts, pbvh->mloop)) { + if (!paint_is_face_hidden(lt, pbvh->hide_vert, pbvh->mloop)) { has_visible = true; } } @@ -562,6 +562,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, pbvh->verts = verts; BKE_mesh_vertex_normals_ensure(mesh); pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(mesh); + pbvh->hide_vert = (bool *)CustomData_get_layer_named(&mesh->vdata, CD_PROP_BOOL, ".hide_vert"); pbvh->vert_bitmap = MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap"); pbvh->totvert = totvert; pbvh->leaf_limit = LEAF_LIMIT; @@ -1316,7 +1317,6 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, case PBVH_FACES: node->draw_buffers = GPU_pbvh_mesh_buffers_build( pbvh->mesh, - pbvh->verts, pbvh->looptri, CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS), node->prim_indices, @@ -1590,9 +1590,12 @@ static void pbvh_faces_node_visibility_update(PBVH *pbvh, PBVHNode *node) BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert); BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); + if (pbvh->hide_vert == NULL) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } for (i = 0; i < totvert; i++) { - MVert *v = &mvert[vert_indices[i]]; - if (!(v->flag & ME_HIDE)) { + if (!(pbvh->hide_vert[vert_indices[i]])) { BKE_pbvh_node_fully_hidden_set(node, false); return; } @@ -2291,7 +2294,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, const MLoopTri *lt = &pbvh->looptri[faces[i]]; const int *face_verts = node->face_vert_indices[i]; - if (pbvh->respect_hide && paint_is_face_hidden(lt, vert, mloop)) { + if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) { continue; } @@ -2600,7 +2603,7 @@ static bool pbvh_faces_node_nearest_to_ray(PBVH *pbvh, const MLoopTri *lt = &pbvh->looptri[faces[i]]; const int *face_verts = node->face_vert_indices[i]; - if (pbvh->respect_hide && paint_is_face_hidden(lt, vert, mloop)) { + if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) { continue; } @@ -3127,6 +3130,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->mask = NULL; if (pbvh->header.type == PBVH_FACES) { vi->vert_normals = pbvh->vert_normals; + vi->hide_vert = pbvh->hide_vert; vi->vmask = CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK); } @@ -3207,6 +3211,27 @@ const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3] return pbvh->vert_normals; } +const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + return pbvh->hide_vert; +} + +bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + if (pbvh->hide_vert) { + return pbvh->hide_vert; + } + pbvh->hide_vert = CustomData_get_layer_named(&pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + if (pbvh->hide_vert) { + return pbvh->hide_vert; + } + pbvh->hide_vert = (bool *)CustomData_add_layer_named( + &pbvh->mesh->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, pbvh->mesh->totvert, ".hide_vert"); + return pbvh->hide_vert; +} + void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg) { pbvh->subdiv_ccg = subdiv_ccg; diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 0edf224e5ff..5babfd3acbe 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -144,10 +144,11 @@ struct PBVH { int leaf_limit; /* Mesh data */ - const struct Mesh *mesh; + struct Mesh *mesh; /* NOTE: Normals are not `const` because they can be updated for drawing by sculpt code. */ float (*vert_normals)[3]; + bool *hide_vert; struct MVert *verts; const struct MPoly *mpoly; const struct MLoop *mloop; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index aaa6baac1ff..4d41471e1fb 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -2501,7 +2501,7 @@ static bool check_rendered_viewport_visible(Main *bmain) return false; } -/* TODO(campbell): shouldn't we be able to use 'DEG_get_view_layer' here? +/* TODO(@campbellbarton): shouldn't we be able to use 'DEG_get_view_layer' here? * Currently this is nullptr on load, so don't. */ static void prepare_mesh_for_viewport_render(Main *bmain, const ViewLayer *view_layer) { diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c index 51ebd232978..745bd2a97e6 100644 --- a/source/blender/blenkernel/intern/shader_fx.c +++ b/source/blender/blenkernel/intern/shader_fx.c @@ -70,7 +70,8 @@ ShaderFxData *BKE_shaderfx_new(int type) fx->type = type; fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render; fx->flag = eShaderFxFlag_OverrideLibrary_Local; - fx->ui_expand_flag = 1; /* Expand only the parent panel by default. */ + /* Expand only the parent panel by default. */ + fx->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; if (fxi->flags & eShaderFxTypeFlag_EnableInEditmode) { fx->mode |= eShaderFxMode_Editmode; diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c index 12a5f00a68b..9c6d507d42c 100644 --- a/source/blender/blenkernel/intern/subdiv_converter_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c @@ -205,7 +205,15 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la mesh->totloop, sizeof(int), "loop uv vertex index"); } UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create( - mpoly, mloop, mloopuv, num_poly, num_vert, limit, false, true); + mpoly, + (const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"), + mloop, + mloopuv, + num_poly, + num_vert, + limit, + false, + true); /* NOTE: First UV vertex is supposed to be always marked as separate. */ storage->num_uv_coordinates = -1; for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) { diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 966ef0af1fc..03d61469bd1 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -284,7 +284,8 @@ static int ss_sync_from_uv(CCGSubSurf *ss, * UV map in really simple cases with mirror + subsurf, see second part of T44530. * Also, initially intention is to treat merged vertices from mirror modifier as seams. * This fixes a very old regression (2.49 was correct here) */ - vmap = BKE_mesh_uv_vert_map_create(mpoly, mloop, mloopuv, totface, totvert, limit, false, true); + vmap = BKE_mesh_uv_vert_map_create( + mpoly, NULL, mloop, mloopuv, totface, totvert, limit, false, true); if (!vmap) { return 0; } diff --git a/source/blender/blenlib/BLI_cpp_type.hh b/source/blender/blenlib/BLI_cpp_type.hh index cc48b456da7..8cf5ead1c7b 100644 --- a/source/blender/blenlib/BLI_cpp_type.hh +++ b/source/blender/blenlib/BLI_cpp_type.hh @@ -20,7 +20,7 @@ * cost of longer compile time, a larger binary and the complexity that comes from using * templates). * - If the code is not performance sensitive, it usually makes sense to use #CPPType instead. - * - Sometimes a combination can make sense. Optimized code can be be generated at compile-time for + * - Sometimes a combination can make sense. Optimized code can be generated at compile-time for * some types, while there is a fallback code path using #CPPType for all other types. * #CPPType::to_static_type allows dispatching between both versions based on the type. * diff --git a/source/blender/blenlib/BLI_scanfill.h b/source/blender/blenlib/BLI_scanfill.h index 04ac7cb05e4..b5b100ac27d 100644 --- a/source/blender/blenlib/BLI_scanfill.h +++ b/source/blender/blenlib/BLI_scanfill.h @@ -82,7 +82,7 @@ struct ScanFillEdge *BLI_scanfill_edge_add(ScanFillContext *sf_ctx, struct ScanFillVert *v2); enum { - /* NOTE(campbell): using BLI_SCANFILL_CALC_REMOVE_DOUBLES + /* NOTE(@campbellbarton): using #BLI_SCANFILL_CALC_REMOVE_DOUBLES * Assumes ordered edges, otherwise we risk an eternal loop * removing double verts. */ BLI_SCANFILL_CALC_REMOVE_DOUBLES = (1 << 1), diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index e7ccdeab80a..773aac95193 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -1667,8 +1667,8 @@ bool isect_ray_tri_v3(const float ray_origin[3], float *r_lambda, float r_uv[2]) { - /* NOTE(campbell): these values were 0.000001 in 2.4x but for projection snapping on - * a human head (1BU == 1m), subsurf level 2, this gave many errors. */ + /* NOTE(@campbellbarton): these values were 0.000001 in 2.4x but for projection snapping on + * a human head `(1BU == 1m)`, subdivision-surface level 2, this gave many errors. */ const float epsilon = 0.00000001f; float p[3], s[3], e1[3], e2[3], q[3]; float a, f, u, v; @@ -3773,7 +3773,7 @@ void barycentric_weights_v2_quad(const float v1[2], const float co[2], float w[4]) { - /* NOTE(campbell): fabsf() here is not needed for convex quads + /* NOTE(@campbellbarton): fabsf() here is not needed for convex quads * (and not used in #interp_weights_poly_v2). * But in the case of concave/bow-tie quads for the mask rasterizer it * gives unreliable results without adding `absf()`. If this becomes an issue for more general diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 357dba154af..0d8ad1da582 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -1675,7 +1675,7 @@ static Edge find_good_sorting_edge(const Vert *testp, * The algorithm is similar to the one for find_ambient_cell, except that * instead of an arbitrary point known to be outside the whole mesh, we * have a particular point (v) and we just want to determine the patches - * that that point is between in sorting-around-an-edge order. + * that point is between in sorting-around-an-edge order. */ static int find_containing_cell(const Vert *v, int t, diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c index 2d76f662611..8263f8ff34e 100644 --- a/source/blender/blenlib/intern/smallhash.c +++ b/source/blender/blenlib/intern/smallhash.c @@ -329,8 +329,7 @@ void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr /** \name Debugging & Introspection * \{ */ -/* NOTE(campbell): this was called _print_smhash in knifetool.c - * it may not be intended for general use. */ +/* NOTE(@campbellbarton): useful for debugging but may not be intended for general use. */ #if 0 void BLI_smallhash_print(SmallHash *sh) { diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 0cbf62cce03..93045bd3680 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -403,7 +403,7 @@ int BLI_str_utf8_char_width_safe(const char *p) /* copied from glib's gutf8.c, added 'Err' arg */ -/* NOTE(campbell): glib uses uint for unicode, best we do the same, +/* NOTE(@campbellbarton): glib uses uint for unicode, best we do the same, * though we don't typedef it. */ #define UTF8_COMPUTE(Char, Mask, Len, Err) \ diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index ff72bfe95b8..c79eb2d530b 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -926,7 +926,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { if (md->mode & eModifierMode_Expanded_DEPRECATED) { - md->ui_expand_flag = 1; + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; } else { md->ui_expand_flag = 0; @@ -954,7 +954,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { LISTBASE_FOREACH (bConstraint *, con, &object->constraints) { if (con->flag & CONSTRAINT_EXPAND_DEPRECATED) { - con->ui_expand_flag = 1; + con->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; } else { con->ui_expand_flag = 0; @@ -968,7 +968,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { LISTBASE_FOREACH (GpencilModifierData *, md, &object->greasepencil_modifiers) { if (md->mode & eGpencilModifierMode_Expanded_DEPRECATED) { - md->ui_expand_flag = 1; + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; } else { md->ui_expand_flag = 0; @@ -982,7 +982,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { LISTBASE_FOREACH (ShaderFxData *, fx, &object->shader_fx) { if (fx->mode & eShaderFxMode_Expanded_DEPRECATED) { - fx->ui_expand_flag = 1; + fx->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; } else { fx->ui_expand_flag = 0; diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index a7637d2712c..0ee5545527b 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -512,16 +512,24 @@ void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst, for (int i = 0; i < me_src_array_len; i++) { const Mesh *me_src = me_src_array[i]; if (i == 0) { - CustomData_copy(&me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); - CustomData_copy(&me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); - CustomData_copy(&me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); - CustomData_copy(&me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh( + &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh( + &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh( + &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh( + &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); } else { - CustomData_merge(&me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); - CustomData_merge(&me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); - CustomData_merge(&me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); - CustomData_merge(&me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); + CustomData_merge_mesh_to_bmesh( + &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); + CustomData_merge_mesh_to_bmesh( + &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); + CustomData_merge_mesh_to_bmesh( + &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); + CustomData_merge_mesh_to_bmesh( + &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); } cd_flag |= me_src->cd_flag; @@ -714,26 +722,25 @@ BMesh *BM_mesh_copy(BMesh *bm_old) char BM_vert_flag_from_mflag(const char mflag) { - return (((mflag & SELECT) ? BM_ELEM_SELECT : 0) | ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)); + return ((mflag & SELECT) ? BM_ELEM_SELECT : 0); } char BM_edge_flag_from_mflag(const short mflag) { return (((mflag & SELECT) ? BM_ELEM_SELECT : 0) | ((mflag & ME_SEAM) ? BM_ELEM_SEAM : 0) | ((mflag & ME_EDGEDRAW) ? BM_ELEM_DRAW : 0) | - ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0) | /* invert */ - ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)); + ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0)); } char BM_face_flag_from_mflag(const char mflag) { return (((mflag & ME_FACE_SEL) ? BM_ELEM_SELECT : 0) | - ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0) | ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)); + ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0)); } char BM_vert_flag_to_mflag(BMVert *v) { const char hflag = v->head.hflag; - return (((hflag & BM_ELEM_SELECT) ? SELECT : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0)); + return (((hflag & BM_ELEM_SELECT) ? SELECT : 0)); } short BM_edge_flag_to_mflag(BMEdge *e) @@ -743,7 +750,6 @@ short BM_edge_flag_to_mflag(BMEdge *e) return (((hflag & BM_ELEM_SELECT) ? SELECT : 0) | ((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) | ((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) | ((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) | - ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) | (BM_edge_is_wire(e) ? ME_LOOSEEDGE : 0) | /* not typical */ ME_EDGERENDER); } @@ -752,5 +758,5 @@ char BM_face_flag_to_mflag(BMFace *f) const char hflag = f->head.hflag; return (((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) | - ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0)); + ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0)); } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index c16d874e3ec..4f42ce4a470 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -88,19 +88,19 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm) BMVert_OFlag *v_olfag; BLI_mempool *toolflagpool = bm->vtoolflagpool; BM_ITER_MESH (v_olfag, &iter, bm, BM_VERTS_OF_MESH) { - v_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool); + v_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool)); } BMEdge_OFlag *e_olfag; toolflagpool = bm->etoolflagpool; BM_ITER_MESH (e_olfag, &iter, bm, BM_EDGES_OF_MESH) { - e_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool); + e_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool)); } BMFace_OFlag *f_olfag; toolflagpool = bm->ftoolflagpool; BM_ITER_MESH (f_olfag, &iter, bm, BM_FACES_OF_MESH) { - f_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool); + f_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool)); } bm->totflags = 1; @@ -125,7 +125,7 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm) BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreateParams *params) { /* allocate the structure */ - BMesh *bm = (BMesh *)MEM_callocN(sizeof(BMesh), __func__); + BMesh *bm = static_cast<BMesh *>(MEM_callocN(sizeof(BMesh), __func__)); /* allocate the memory pools for the mesh elements */ bm_mempool_init(bm, allocsize, params->use_toolflags); @@ -262,7 +262,7 @@ void BM_mesh_free(BMesh *bm) if (bm->py_handle) { /* keep this out of 'BM_mesh_data_free' because we want python * to be able to clear the mesh and maintain access. */ - bpy_bm_generic_invalidate((BPy_BMGeneric *)bm->py_handle); + bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(bm->py_handle)); bm->py_handle = nullptr; } @@ -581,7 +581,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype) if (bm->vtable) { MEM_freeN(bm->vtable); } - bm->vtable = (BMVert **)MEM_mallocN(sizeof(void **) * bm->totvert, "bm->vtable"); + bm->vtable = static_cast<BMVert **>( + MEM_mallocN(sizeof(void **) * bm->totvert, "bm->vtable")); bm->vtable_tot = bm->totvert; } BM_iter_as_array(bm, BM_VERTS_OF_MESH, nullptr, (void **)bm->vtable, bm->totvert); @@ -594,7 +595,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype) if (bm->etable) { MEM_freeN(bm->etable); } - bm->etable = (BMEdge **)MEM_mallocN(sizeof(void **) * bm->totedge, "bm->etable"); + bm->etable = static_cast<BMEdge **>( + MEM_mallocN(sizeof(void **) * bm->totedge, "bm->etable")); bm->etable_tot = bm->totedge; } BM_iter_as_array(bm, BM_EDGES_OF_MESH, nullptr, (void **)bm->etable, bm->totedge); @@ -607,7 +609,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype) if (bm->ftable) { MEM_freeN(bm->ftable); } - bm->ftable = (BMFace **)MEM_mallocN(sizeof(void **) * bm->totface, "bm->ftable"); + bm->ftable = static_cast<BMFace **>( + MEM_mallocN(sizeof(void **) * bm->totface, "bm->ftable")); bm->ftable_tot = bm->totface; } BM_iter_as_array(bm, BM_FACES_OF_MESH, nullptr, (void **)bm->ftable, bm->totface); @@ -647,17 +650,17 @@ void BM_mesh_elem_table_free(BMesh *bm, const char htype) BMVert *BM_vert_at_index_find(BMesh *bm, const int index) { - return (BMVert *)BLI_mempool_findelem(bm->vpool, index); + return static_cast<BMVert *>(BLI_mempool_findelem(bm->vpool, index)); } BMEdge *BM_edge_at_index_find(BMesh *bm, const int index) { - return (BMEdge *)BLI_mempool_findelem(bm->epool, index); + return static_cast<BMEdge *>(BLI_mempool_findelem(bm->epool, index)); } BMFace *BM_face_at_index_find(BMesh *bm, const int index) { - return (BMFace *)BLI_mempool_findelem(bm->fpool, index); + return static_cast<BMFace *>(BLI_mempool_findelem(bm->fpool, index)); } BMLoop *BM_loop_at_index_find(BMesh *bm, const int index) @@ -754,16 +757,17 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const /* Make a copy of all vertices. */ verts_pool = bm->vtable; - verts_copy = (BMVert *)MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy"); + verts_copy = static_cast<BMVert *>( + MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy")); void **pyptrs = (cd_vert_pyptr != -1) ? - (void **)MEM_mallocN(sizeof(void *) * totvert, __func__) : + static_cast<void **>(MEM_mallocN(sizeof(void *) * totvert, __func__)) : nullptr; for (i = totvert, ve = verts_copy + totvert - 1, vep = verts_pool + totvert - 1; i--; ve--, vep--) { *ve = **vep; // printf("*vep: %p, verts_pool[%d]: %p\n", *vep, i, verts_pool[i]); if (cd_vert_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr); + void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr)); pyptrs[i] = *pyptr; } } @@ -781,7 +785,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const #endif BLI_ghash_insert(vptr_map, *vep, new_vep); if (cd_vert_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr); + void **pyptr = static_cast<void **>( + BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr)); *pyptr = pyptrs[*new_idx]; } } @@ -808,15 +813,16 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const /* Make a copy of all vertices. */ edges_pool = bm->etable; - edges_copy = (BMEdge *)MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy"); + edges_copy = static_cast<BMEdge *>( + MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy")); void **pyptrs = (cd_edge_pyptr != -1) ? - (void **)MEM_mallocN(sizeof(void *) * totedge, __func__) : + static_cast<void **>(MEM_mallocN(sizeof(void *) * totedge, __func__)) : nullptr; for (i = totedge, ed = edges_copy + totedge - 1, edp = edges_pool + totedge - 1; i--; ed--, edp--) { *ed = **edp; if (cd_edge_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr); + void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr)); pyptrs[i] = *pyptr; } } @@ -834,7 +840,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const "mapping edge from %d to %d (%p/%p to %p)\n", i, *new_idx, *edp, edges_pool[i], new_edp); #endif if (cd_edge_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr); + void **pyptr = static_cast<void **>( + BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr)); *pyptr = pyptrs[*new_idx]; } } @@ -861,15 +868,16 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const /* Make a copy of all vertices. */ faces_pool = bm->ftable; - faces_copy = (BMFace *)MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy"); + faces_copy = static_cast<BMFace *>( + MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy")); void **pyptrs = (cd_poly_pyptr != -1) ? - (void **)MEM_mallocN(sizeof(void *) * totface, __func__) : + static_cast<void **>(MEM_mallocN(sizeof(void *) * totface, __func__)) : nullptr; for (i = totface, fa = faces_copy + totface - 1, fap = faces_pool + totface - 1; i--; fa--, fap--) { *fa = **fap; if (cd_poly_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr); + void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr)); pyptrs[i] = *pyptr; } } @@ -883,7 +891,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const *new_fap = *fa; BLI_ghash_insert(fptr_map, *fap, new_fap); if (cd_poly_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr); + void **pyptr = static_cast<void **>( + BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr)); *pyptr = pyptrs[*new_idx]; } } @@ -903,7 +912,7 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BM_ITER_MESH (ve, &iter, bm, BM_VERTS_OF_MESH) { // printf("Vert e: %p -> %p\n", ve->e, BLI_ghash_lookup(eptr_map, ve->e)); if (ve->e) { - ve->e = (BMEdge *)BLI_ghash_lookup(eptr_map, ve->e); + ve->e = static_cast<BMEdge *>(BLI_ghash_lookup(eptr_map, ve->e)); BLI_assert(ve->e); } } @@ -919,8 +928,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const printf("Edge v1: %p -> %p\n", ed->v1, BLI_ghash_lookup(vptr_map, ed->v1)); printf("Edge v2: %p -> %p\n", ed->v2, BLI_ghash_lookup(vptr_map, ed->v2)); #endif - ed->v1 = (BMVert *)BLI_ghash_lookup(vptr_map, ed->v1); - ed->v2 = (BMVert *)BLI_ghash_lookup(vptr_map, ed->v2); + ed->v1 = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, ed->v1)); + ed->v2 = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, ed->v2)); BLI_assert(ed->v1); BLI_assert(ed->v2); } @@ -939,10 +948,14 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const ed->v2_disk_link.next, BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next)); #endif - ed->v1_disk_link.prev = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev); - ed->v1_disk_link.next = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next); - ed->v2_disk_link.prev = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev); - ed->v2_disk_link.next = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next); + ed->v1_disk_link.prev = static_cast<BMEdge *>( + BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev)); + ed->v1_disk_link.next = static_cast<BMEdge *>( + BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next)); + ed->v2_disk_link.prev = static_cast<BMEdge *>( + BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev)); + ed->v2_disk_link.next = static_cast<BMEdge *>( + BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next)); BLI_assert(ed->v1_disk_link.prev); BLI_assert(ed->v1_disk_link.next); BLI_assert(ed->v2_disk_link.prev); @@ -956,17 +969,17 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BM_ITER_ELEM (lo, &iterl, fa, BM_LOOPS_OF_FACE) { if (vptr_map) { // printf("Loop v: %p -> %p\n", lo->v, BLI_ghash_lookup(vptr_map, lo->v)); - lo->v = (BMVert *)BLI_ghash_lookup(vptr_map, lo->v); + lo->v = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, lo->v)); BLI_assert(lo->v); } if (eptr_map) { // printf("Loop e: %p -> %p\n", lo->e, BLI_ghash_lookup(eptr_map, lo->e)); - lo->e = (BMEdge *)BLI_ghash_lookup(eptr_map, lo->e); + lo->e = static_cast<BMEdge *>(BLI_ghash_lookup(eptr_map, lo->e)); BLI_assert(lo->e); } if (fptr_map) { // printf("Loop f: %p -> %p\n", lo->f, BLI_ghash_lookup(fptr_map, lo->f)); - lo->f = (BMFace *)BLI_ghash_lookup(fptr_map, lo->f); + lo->f = static_cast<BMFace *>(BLI_ghash_lookup(fptr_map, lo->f)); BLI_assert(lo->f); } } @@ -975,23 +988,23 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const /* Selection history */ { BMEditSelection *ese; - for (ese = (BMEditSelection *)bm->selected.first; ese; ese = ese->next) { + for (ese = static_cast<BMEditSelection *>(bm->selected.first); ese; ese = ese->next) { switch (ese->htype) { case BM_VERT: if (vptr_map) { - ese->ele = (BMElem *)BLI_ghash_lookup(vptr_map, ese->ele); + ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(vptr_map, ese->ele)); BLI_assert(ese->ele); } break; case BM_EDGE: if (eptr_map) { - ese->ele = (BMElem *)BLI_ghash_lookup(eptr_map, ese->ele); + ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(eptr_map, ese->ele)); BLI_assert(ese->ele); } break; case BM_FACE: if (fptr_map) { - ese->ele = (BMElem *)BLI_ghash_lookup(fptr_map, ese->ele); + ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(fptr_map, ese->ele)); BLI_assert(ese->ele); } break; @@ -1001,7 +1014,7 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const if (fptr_map) { if (bm->act_face) { - bm->act_face = (BMFace *)BLI_ghash_lookup(fptr_map, bm->act_face); + bm->act_face = static_cast<BMFace *>(BLI_ghash_lookup(fptr_map, bm->act_face)); BLI_assert(bm->act_face); } } @@ -1027,18 +1040,18 @@ void BM_mesh_rebuild(BMesh *bm, const char remap = (vpool_dst ? BM_VERT : 0) | (epool_dst ? BM_EDGE : 0) | (lpool_dst ? BM_LOOP : 0) | (fpool_dst ? BM_FACE : 0); - BMVert **vtable_dst = (remap & BM_VERT) ? - (BMVert **)MEM_mallocN(bm->totvert * sizeof(BMVert *), __func__) : - nullptr; - BMEdge **etable_dst = (remap & BM_EDGE) ? - (BMEdge **)MEM_mallocN(bm->totedge * sizeof(BMEdge *), __func__) : - nullptr; - BMLoop **ltable_dst = (remap & BM_LOOP) ? - (BMLoop **)MEM_mallocN(bm->totloop * sizeof(BMLoop *), __func__) : - nullptr; - BMFace **ftable_dst = (remap & BM_FACE) ? - (BMFace **)MEM_mallocN(bm->totface * sizeof(BMFace *), __func__) : - nullptr; + BMVert **vtable_dst = (remap & BM_VERT) ? static_cast<BMVert **>(MEM_mallocN( + sizeof(BMVert *) * bm->totvert, __func__)) : + nullptr; + BMEdge **etable_dst = (remap & BM_EDGE) ? static_cast<BMEdge **>(MEM_mallocN( + sizeof(BMEdge *) * bm->totedge, __func__)) : + nullptr; + BMLoop **ltable_dst = (remap & BM_LOOP) ? static_cast<BMLoop **>(MEM_mallocN( + sizeof(BMLoop *) * bm->totloop, __func__)) : + nullptr; + BMFace **ftable_dst = (remap & BM_FACE) ? static_cast<BMFace **>(MEM_mallocN( + sizeof(BMFace *) * bm->totface, __func__)) : + nullptr; const bool use_toolflags = params->use_toolflags; @@ -1047,12 +1060,13 @@ void BM_mesh_rebuild(BMesh *bm, int index; BMVert *v_src; BM_ITER_MESH_INDEX (v_src, &iter, bm, BM_VERTS_OF_MESH, index) { - BMVert *v_dst = (BMVert *)BLI_mempool_alloc(vpool_dst); + BMVert *v_dst = static_cast<BMVert *>(BLI_mempool_alloc(vpool_dst)); memcpy(v_dst, v_src, sizeof(BMVert)); if (use_toolflags) { - ((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc( - bm->vtoolflagpool) : - nullptr; + ((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ? + static_cast<BMFlagLayer *>( + BLI_mempool_calloc(bm->vtoolflagpool)) : + nullptr; } vtable_dst[index] = v_dst; @@ -1065,12 +1079,13 @@ void BM_mesh_rebuild(BMesh *bm, int index; BMEdge *e_src; BM_ITER_MESH_INDEX (e_src, &iter, bm, BM_EDGES_OF_MESH, index) { - BMEdge *e_dst = (BMEdge *)BLI_mempool_alloc(epool_dst); + BMEdge *e_dst = static_cast<BMEdge *>(BLI_mempool_alloc(epool_dst)); memcpy(e_dst, e_src, sizeof(BMEdge)); if (use_toolflags) { - ((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc( - bm->etoolflagpool) : - nullptr; + ((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ? + static_cast<BMFlagLayer *>( + BLI_mempool_calloc(bm->etoolflagpool)) : + nullptr; } etable_dst[index] = e_dst; @@ -1085,12 +1100,13 @@ void BM_mesh_rebuild(BMesh *bm, BM_ITER_MESH_INDEX (f_src, &iter, bm, BM_FACES_OF_MESH, index) { if (remap & BM_FACE) { - BMFace *f_dst = (BMFace *)BLI_mempool_alloc(fpool_dst); + BMFace *f_dst = static_cast<BMFace *>(BLI_mempool_alloc(fpool_dst)); memcpy(f_dst, f_src, sizeof(BMFace)); if (use_toolflags) { - ((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc( - bm->ftoolflagpool) : - nullptr; + ((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ? + static_cast<BMFlagLayer *>( + BLI_mempool_calloc(bm->ftoolflagpool)) : + nullptr; } ftable_dst[index] = f_dst; @@ -1102,7 +1118,7 @@ void BM_mesh_rebuild(BMesh *bm, BMLoop *l_iter_src, *l_first_src; l_iter_src = l_first_src = BM_FACE_FIRST_LOOP((BMFace *)f_src); do { - BMLoop *l_dst = (BMLoop *)BLI_mempool_alloc(lpool_dst); + BMLoop *l_dst = static_cast<BMLoop *>(BLI_mempool_alloc(lpool_dst)); memcpy(l_dst, l_iter_src, sizeof(BMLoop)); ltable_dst[index_loop] = l_dst; BM_elem_index_set(l_iter_src, index_loop++); /* set_ok */ @@ -1319,7 +1335,8 @@ void BM_mesh_vert_coords_get(BMesh *bm, float (*vert_coords)[3]) float (*BM_mesh_vert_coords_alloc(BMesh *bm, int *r_vert_len))[3] { - float(*vert_coords)[3] = (float(*)[3])MEM_mallocN(bm->totvert * sizeof(*vert_coords), __func__); + float(*vert_coords)[3] = static_cast<float(*)[3]>( + MEM_mallocN(bm->totvert * sizeof(*vert_coords), __func__)); BM_mesh_vert_coords_get(bm, vert_coords); *r_vert_len = bm->totvert; return vert_coords; diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index a5994b52bc2..d766a26cf6e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -17,7 +17,7 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); struct BMeshCreateParams { - bool use_toolflags : true; + bool use_toolflags : 1; }; /** diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 739e474c59a..eadf87d7ebd 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -83,7 +83,10 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_span.hh" +#include "BLI_string_ref.hh" +#include "BLI_task.hh" +#include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -103,7 +106,9 @@ static CLG_LogRef LOG = {"bmesh.mesh.convert"}; using blender::Array; using blender::IndexRange; +using blender::MutableSpan; using blender::Span; +using blender::StringRef; void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag) { @@ -184,10 +189,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar if (!me || !me->totvert) { if (me && is_new) { /* No verts? still copy custom-data layout. */ - CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0); - CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0); - CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0); - CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0); + CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0); + CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0); + CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0); + CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0); CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); @@ -203,10 +208,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar } if (is_new) { - CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, 0); - CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_CALLOC, 0); - CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_CALLOC, 0); - CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_CALLOC, 0); } else { CustomData_bmesh_merge(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, bm, BM_VERT); @@ -318,6 +323,13 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; + const bool *hide_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert"); + const bool *hide_edge = (const bool *)CustomData_get_layer_named( + &me->edata, CD_PROP_BOOL, ".hide_edge"); + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &me->pdata, CD_PROP_BOOL, ".hide_poly"); + Span<MVert> mvert{me->mvert, me->totvert}; Array<BMVert *> vtable(me->totvert); for (const int i : mvert.index_range()) { @@ -327,6 +339,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Transfer flag. */ v->head.hflag = BM_vert_flag_from_mflag(mvert[i].flag & ~SELECT); + if (hide_vert && hide_vert[i]) { + BM_elem_flag_enable(v, BM_ELEM_HIDDEN); + } /* This is necessary for selection counts to work properly. */ if (mvert[i].flag & SELECT) { @@ -366,6 +381,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Transfer flags. */ e->head.hflag = BM_edge_flag_from_mflag(medge[i].flag & ~SELECT); + if (hide_edge && hide_edge[i]) { + BM_elem_flag_enable(e, BM_ELEM_HIDDEN); + } /* This is necessary for selection counts to work properly. */ if (medge[i].flag & SELECT) { @@ -416,6 +434,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Transfer flag. */ f->head.hflag = BM_face_flag_from_mflag(mpoly[i].flag & ~ME_FACE_SEL); + if (hide_poly && hide_poly[i]) { + BM_elem_flag_enable(f, BM_ELEM_HIDDEN); + } /* This is necessary for selection counts to work properly. */ if (mpoly[i].flag & ME_FACE_SEL) { @@ -861,6 +882,63 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) } } +template<typename GetFn> +static void write_elem_flag_to_attribute(blender::bke::MutableAttributeAccessor &attributes, + const StringRef attribute_name, + const eAttrDomain domain, + const bool do_write, + const GetFn &get_fn) +{ + using namespace blender; + if (do_write) { + bke::SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_only_span<bool>( + attribute_name, domain); + threading::parallel_for(attribute.span.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + attribute.span[i] = get_fn(i); + } + }); + attribute.finish(); + } + else { + /* To avoid overhead, remove the hide attribute if possible. */ + attributes.remove(attribute_name); + } +} + +static void convert_bmesh_hide_flags_to_mesh_attributes(BMesh &bm, + const bool need_hide_vert, + const bool need_hide_edge, + const bool need_hide_poly, + Mesh &mesh) +{ + using namespace blender; + /* The "hide" attributes are stored as flags on #BMesh. */ + BLI_assert(CustomData_get_layer_named(&bm.vdata, CD_PROP_BOOL, ".hide_vert") == nullptr); + BLI_assert(CustomData_get_layer_named(&bm.edata, CD_PROP_BOOL, ".hide_edge") == nullptr); + BLI_assert(CustomData_get_layer_named(&bm.pdata, CD_PROP_BOOL, ".hide_poly") == nullptr); + + if (!(need_hide_vert || need_hide_edge || need_hide_poly)) { + return; + } + + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + BM_mesh_elem_table_ensure(&bm, BM_VERT | BM_EDGE | BM_FACE); + + write_elem_flag_to_attribute( + attributes, ".hide_vert", ATTR_DOMAIN_POINT, need_hide_vert, [&](const int i) { + return BM_elem_flag_test(BM_vert_at_index(&bm, i), BM_ELEM_HIDDEN); + }); + write_elem_flag_to_attribute( + attributes, ".hide_edge", ATTR_DOMAIN_EDGE, need_hide_edge, [&](const int i) { + return BM_elem_flag_test(BM_edge_at_index(&bm, i), BM_ELEM_HIDDEN); + }); + write_elem_flag_to_attribute( + attributes, ".hide_poly", ATTR_DOMAIN_FACE, need_hide_poly, [&](const int i) { + return BM_elem_flag_test(BM_face_at_index(&bm, i), BM_ELEM_HIDDEN); + }); +} + void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) { MEdge *med; @@ -895,10 +973,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh { CustomData_MeshMasks mask = CD_MASK_MESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert); - CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge); - CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop); - CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly); + CustomData_copy_mesh_to_bmesh(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert); + CustomData_copy_mesh_to_bmesh(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge); + CustomData_copy_mesh_to_bmesh(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop); + CustomData_copy_mesh_to_bmesh(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly); } MVert *mvert = bm->totvert ? (MVert *)MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") : @@ -915,6 +993,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); + bool need_hide_vert = false; + bool need_hide_edge = false; + bool need_hide_poly = false; + /* Clear normals on the mesh completely, since the original vertex and polygon count might be * different than the BMesh's. */ BKE_mesh_clear_derived_normals(me); @@ -929,6 +1011,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh copy_v3_v3(mvert->co, v->co); mvert->flag = BM_vert_flag_to_mflag(v); + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + need_hide_vert = true; + } BM_elem_index_set(v, i); /* set_inline */ @@ -949,6 +1034,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh med->v2 = BM_elem_index_get(e->v2); med->flag = BM_edge_flag_to_mflag(e); + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + need_hide_edge = true; + } BM_elem_index_set(e, i); /* set_inline */ @@ -975,6 +1063,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh mpoly->totloop = f->len; mpoly->mat_nr = f->mat_nr; mpoly->flag = BM_face_flag_to_mflag(f); + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + need_hide_poly = true; + } l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { @@ -1067,6 +1158,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } + convert_bmesh_hide_flags_to_mesh_attributes( + *bm, need_hide_vert, need_hide_edge, need_hide_poly, *me); + BKE_mesh_update_customdata_pointers(me, false); { @@ -1158,6 +1252,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); + bool need_hide_vert = false; + bool need_hide_edge = false; + bool need_hide_poly = false; + /* Clear normals on the mesh completely, since the original vertex and polygon count might be * different than the BMesh's. */ BKE_mesh_clear_derived_normals(me); @@ -1172,6 +1270,13 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * BM_elem_index_set(eve, i); /* set_inline */ mv->flag = BM_vert_flag_to_mflag(eve); + if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + need_hide_vert = true; + } + + if (cd_vert_bweight_offset != -1) { + mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset); + } CustomData_from_bmesh_block(&bm->vdata, &me->vdata, eve->head.data, i); } @@ -1186,6 +1291,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * med->v2 = BM_elem_index_get(eed->v2); med->flag = BM_edge_flag_to_mflag(eed); + if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + need_hide_edge = true; + } /* Handle this differently to editmode switching, * only enable draw for single user edges rather than calculating angle. */ @@ -1213,6 +1321,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * mp->totloop = efa->len; mp->flag = BM_face_flag_to_mflag(efa); + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + need_hide_poly = true; + } + mp->loopstart = j; mp->mat_nr = efa->mat_nr; @@ -1232,5 +1344,8 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); + convert_bmesh_hide_flags_to_mesh_attributes( + *bm, need_hide_vert, need_hide_edge, need_hide_poly, *me); + me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); } diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 55e349423bb..f49a9694ab3 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -1,676 +1,682 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright 2011 Blender Foundation. All rights reserved. -set(INC - . - intern - nodes - operations - ../blenkernel - ../blenlib - ../blentranslation - ../depsgraph - ../imbuf - ../makesdna - ../makesrna - ../nodes - ../windowmanager - ../nodes/composite - ../nodes/intern - ../render - ../render/intern - ../../../extern/clew/include - ../../../intern/atomic - ../../../intern/clog - ../../../intern/guardedalloc - - # dna_type_offsets.h - ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern - # RNA_prototypes.h - ${CMAKE_BINARY_DIR}/source/blender/makesrna -) - -set(INC_SYS - -) - -set(SRC - COM_compositor.h - COM_defines.h - - intern/COM_BufferArea.h - intern/COM_BufferOperation.cc - intern/COM_BufferOperation.h - intern/COM_BufferRange.h - intern/COM_BuffersIterator.h - intern/COM_CPUDevice.cc - intern/COM_CPUDevice.h - intern/COM_ChunkOrder.cc - intern/COM_ChunkOrder.h - intern/COM_ChunkOrderHotspot.cc - intern/COM_ChunkOrderHotspot.h - intern/COM_CompositorContext.cc - intern/COM_CompositorContext.h - intern/COM_ConstantFolder.cc - intern/COM_ConstantFolder.h - intern/COM_Converter.cc - intern/COM_Converter.h - intern/COM_Debug.cc - intern/COM_Debug.h - intern/COM_Device.cc - intern/COM_Device.h - intern/COM_Enums.cc - intern/COM_Enums.h - intern/COM_ExecutionGroup.cc - intern/COM_ExecutionGroup.h - intern/COM_ExecutionModel.cc - intern/COM_ExecutionModel.h - intern/COM_ExecutionSystem.cc - intern/COM_ExecutionSystem.h - intern/COM_FullFrameExecutionModel.cc - intern/COM_FullFrameExecutionModel.h - intern/COM_MemoryBuffer.cc - intern/COM_MemoryBuffer.h - intern/COM_MemoryProxy.cc - intern/COM_MemoryProxy.h - intern/COM_MetaData.cc - intern/COM_MetaData.h - intern/COM_MultiThreadedOperation.cc - intern/COM_MultiThreadedOperation.h - intern/COM_MultiThreadedRowOperation.cc - intern/COM_MultiThreadedRowOperation.h - intern/COM_Node.cc - intern/COM_Node.h - intern/COM_NodeConverter.cc - intern/COM_NodeConverter.h - intern/COM_NodeGraph.cc - intern/COM_NodeGraph.h - intern/COM_NodeOperation.cc - intern/COM_NodeOperation.h - intern/COM_NodeOperationBuilder.cc - intern/COM_NodeOperationBuilder.h - intern/COM_OpenCLDevice.cc - intern/COM_OpenCLDevice.h - intern/COM_SharedOperationBuffers.cc - intern/COM_SharedOperationBuffers.h - intern/COM_SingleThreadedOperation.cc - intern/COM_SingleThreadedOperation.h - intern/COM_TiledExecutionModel.cc - intern/COM_TiledExecutionModel.h - intern/COM_WorkPackage.cc - intern/COM_WorkPackage.h - intern/COM_WorkScheduler.cc - intern/COM_WorkScheduler.h - intern/COM_compositor.cc - - operations/COM_QualityStepHelper.cc - operations/COM_QualityStepHelper.h - - # Internal nodes - nodes/COM_SocketProxyNode.cc - nodes/COM_SocketProxyNode.h - - # input nodes - nodes/COM_BokehImageNode.cc - nodes/COM_BokehImageNode.h - nodes/COM_ColorNode.cc - nodes/COM_ColorNode.h - nodes/COM_ImageNode.cc - nodes/COM_ImageNode.h - nodes/COM_MaskNode.cc - nodes/COM_MaskNode.h - nodes/COM_MovieClipNode.cc - nodes/COM_MovieClipNode.h - nodes/COM_OutputFileNode.cc - nodes/COM_OutputFileNode.h - nodes/COM_RenderLayersNode.cc - nodes/COM_RenderLayersNode.h - nodes/COM_SceneTimeNode.cc - nodes/COM_SceneTimeNode.h - nodes/COM_SwitchNode.cc - nodes/COM_SwitchNode.h - nodes/COM_SwitchViewNode.cc - nodes/COM_SwitchViewNode.h - nodes/COM_TextureNode.cc - nodes/COM_TextureNode.h - nodes/COM_TimeNode.cc - nodes/COM_TimeNode.h - nodes/COM_ValueNode.cc - nodes/COM_ValueNode.h - - # output nodes - nodes/COM_CompositorNode.cc - nodes/COM_CompositorNode.h - nodes/COM_SplitViewerNode.cc - nodes/COM_SplitViewerNode.h - nodes/COM_ViewLevelsNode.cc - nodes/COM_ViewLevelsNode.h - nodes/COM_ViewerNode.cc - nodes/COM_ViewerNode.h - operations/COM_CalculateMeanOperation.cc - operations/COM_CalculateMeanOperation.h - operations/COM_CalculateStandardDeviationOperation.cc - operations/COM_CalculateStandardDeviationOperation.h - - # distort nodes - nodes/COM_FlipNode.cc - nodes/COM_FlipNode.h - nodes/COM_RotateNode.cc - nodes/COM_RotateNode.h - nodes/COM_ScaleNode.cc - nodes/COM_ScaleNode.h - nodes/COM_TranslateNode.cc - nodes/COM_TranslateNode.h - - nodes/COM_DisplaceNode.cc - nodes/COM_DisplaceNode.h - nodes/COM_MapUVNode.cc - nodes/COM_MapUVNode.h - - nodes/COM_ChannelMatteNode.cc - nodes/COM_ChannelMatteNode.h - nodes/COM_ChromaMatteNode.cc - nodes/COM_ChromaMatteNode.h - nodes/COM_ColorMatteNode.cc - nodes/COM_ColorMatteNode.h - nodes/COM_DifferenceMatteNode.cc - nodes/COM_DifferenceMatteNode.h - nodes/COM_DistanceMatteNode.cc - nodes/COM_DistanceMatteNode.h - nodes/COM_LensDistortionNode.cc - nodes/COM_LensDistortionNode.h - nodes/COM_LuminanceMatteNode.cc - nodes/COM_LuminanceMatteNode.h - - nodes/COM_GlareNode.cc - nodes/COM_GlareNode.h - - nodes/COM_SunBeamsNode.cc - nodes/COM_SunBeamsNode.h - operations/COM_SunBeamsOperation.cc - operations/COM_SunBeamsOperation.h - - nodes/COM_CryptomatteNode.cc - nodes/COM_CryptomatteNode.h - operations/COM_CryptomatteOperation.cc - operations/COM_CryptomatteOperation.h - - nodes/COM_CornerPinNode.cc - nodes/COM_CornerPinNode.h - nodes/COM_PlaneTrackDeformNode.cc - nodes/COM_PlaneTrackDeformNode.h - - nodes/COM_CropNode.cc - nodes/COM_CropNode.h - operations/COM_CropOperation.cc - operations/COM_CropOperation.h - - nodes/COM_DefocusNode.cc - nodes/COM_DefocusNode.h - nodes/COM_MovieDistortionNode.cc - nodes/COM_MovieDistortionNode.h - nodes/COM_Stabilize2dNode.cc - nodes/COM_Stabilize2dNode.h - nodes/COM_TransformNode.cc - nodes/COM_TransformNode.h - - # color nodes - nodes/COM_AlphaOverNode.cc - nodes/COM_AlphaOverNode.h - nodes/COM_BrightnessNode.cc - nodes/COM_BrightnessNode.h - nodes/COM_ColorBalanceNode.cc - nodes/COM_ColorBalanceNode.h - nodes/COM_ColorCorrectionNode.cc - nodes/COM_ColorCorrectionNode.h - nodes/COM_ColorCurveNode.cc - nodes/COM_ColorCurveNode.h - nodes/COM_ColorExposureNode.cc - nodes/COM_ColorExposureNode.h - nodes/COM_ColorRampNode.cc - nodes/COM_ColorRampNode.h - nodes/COM_ColorToBWNode.cc - nodes/COM_ColorToBWNode.h - nodes/COM_ConvertAlphaNode.cc - nodes/COM_ConvertAlphaNode.h - nodes/COM_ConvertColorSpaceNode.cc - nodes/COM_ConvertColorSpaceNode.h - nodes/COM_GammaNode.cc - nodes/COM_GammaNode.h - nodes/COM_HueSaturationValueCorrectNode.cc - nodes/COM_HueSaturationValueCorrectNode.h - nodes/COM_HueSaturationValueNode.cc - nodes/COM_HueSaturationValueNode.h - nodes/COM_InvertNode.cc - nodes/COM_InvertNode.h - nodes/COM_MixNode.cc - nodes/COM_MixNode.h - nodes/COM_SetAlphaNode.cc - nodes/COM_SetAlphaNode.h - nodes/COM_TonemapNode.cc - nodes/COM_TonemapNode.h - nodes/COM_VectorCurveNode.cc - nodes/COM_VectorCurveNode.h - nodes/COM_ZCombineNode.cc - nodes/COM_ZCombineNode.h - operations/COM_TonemapOperation.cc - operations/COM_TonemapOperation.h - - # converter nodes - nodes/COM_CombineColorNode.cc - nodes/COM_CombineColorNode.h - nodes/COM_CombineColorNodeLegacy.cc - nodes/COM_CombineColorNodeLegacy.h - nodes/COM_CombineXYZNode.cc - nodes/COM_CombineXYZNode.h - nodes/COM_IDMaskNode.cc - nodes/COM_IDMaskNode.h - nodes/COM_SeparateColorNode.cc - nodes/COM_SeparateColorNode.h - nodes/COM_SeparateColorNodeLegacy.cc - nodes/COM_SeparateColorNodeLegacy.h - nodes/COM_SeparateXYZNode.cc - nodes/COM_SeparateXYZNode.h - - nodes/COM_MapRangeNode.cc - nodes/COM_MapRangeNode.h - nodes/COM_MapValueNode.cc - nodes/COM_MapValueNode.h - nodes/COM_MathNode.cc - nodes/COM_MathNode.h - nodes/COM_NormalNode.cc - nodes/COM_NormalNode.h - nodes/COM_NormalizeNode.cc - nodes/COM_NormalizeNode.h - - operations/COM_NormalizeOperation.cc - operations/COM_NormalizeOperation.h - - nodes/COM_PixelateNode.cc - nodes/COM_PixelateNode.h - operations/COM_PixelateOperation.cc - operations/COM_PixelateOperation.h - - # Filter nodes - nodes/COM_BilateralBlurNode.cc - nodes/COM_BilateralBlurNode.h - operations/COM_BilateralBlurOperation.cc - operations/COM_BilateralBlurOperation.h - nodes/COM_VectorBlurNode.cc - nodes/COM_VectorBlurNode.h - operations/COM_VectorBlurOperation.cc - operations/COM_VectorBlurOperation.h - nodes/COM_AntiAliasingNode.cc - nodes/COM_AntiAliasingNode.h - nodes/COM_BlurNode.cc - nodes/COM_BlurNode.h - nodes/COM_BokehBlurNode.cc - nodes/COM_BokehBlurNode.h - nodes/COM_DenoiseNode.cc - nodes/COM_DenoiseNode.h - nodes/COM_DespeckleNode.cc - nodes/COM_DespeckleNode.h - nodes/COM_DilateErodeNode.cc - nodes/COM_DilateErodeNode.h - nodes/COM_DirectionalBlurNode.cc - nodes/COM_DirectionalBlurNode.h - nodes/COM_FilterNode.cc - nodes/COM_FilterNode.h - nodes/COM_InpaintNode.cc - nodes/COM_InpaintNode.h - nodes/COM_PosterizeNode.cc - nodes/COM_PosterizeNode.h - - operations/COM_BlurBaseOperation.cc - operations/COM_BlurBaseOperation.h - operations/COM_BokehBlurOperation.cc - operations/COM_BokehBlurOperation.h - operations/COM_DirectionalBlurOperation.cc - operations/COM_DirectionalBlurOperation.h - operations/COM_FastGaussianBlurOperation.cc - operations/COM_FastGaussianBlurOperation.h - operations/COM_GammaCorrectOperation.cc - operations/COM_GammaCorrectOperation.h - operations/COM_GaussianAlphaBlurBaseOperation.cc - operations/COM_GaussianAlphaBlurBaseOperation.h - operations/COM_GaussianAlphaXBlurOperation.cc - operations/COM_GaussianAlphaXBlurOperation.h - operations/COM_GaussianAlphaYBlurOperation.cc - operations/COM_GaussianAlphaYBlurOperation.h - operations/COM_GaussianBlurBaseOperation.cc - operations/COM_GaussianBlurBaseOperation.h - operations/COM_GaussianBokehBlurOperation.cc - operations/COM_GaussianBokehBlurOperation.h - operations/COM_GaussianXBlurOperation.cc - operations/COM_GaussianXBlurOperation.h - operations/COM_GaussianYBlurOperation.cc - operations/COM_GaussianYBlurOperation.h - operations/COM_MovieClipAttributeOperation.cc - operations/COM_MovieClipAttributeOperation.h - operations/COM_MovieDistortionOperation.cc - operations/COM_MovieDistortionOperation.h - operations/COM_PosterizeOperation.cc - operations/COM_PosterizeOperation.h - operations/COM_SMAAOperation.cc - operations/COM_SMAAOperation.h - operations/COM_VariableSizeBokehBlurOperation.cc - operations/COM_VariableSizeBokehBlurOperation.h - - # Matte nodes - nodes/COM_BoxMaskNode.cc - nodes/COM_BoxMaskNode.h - nodes/COM_ColorSpillNode.cc - nodes/COM_ColorSpillNode.h - nodes/COM_DoubleEdgeMaskNode.cc - nodes/COM_DoubleEdgeMaskNode.h - nodes/COM_EllipseMaskNode.cc - nodes/COM_EllipseMaskNode.h - - operations/COM_DoubleEdgeMaskOperation.cc - operations/COM_DoubleEdgeMaskOperation.h - - - nodes/COM_KeyingScreenNode.cc - nodes/COM_KeyingScreenNode.h - operations/COM_KeyingScreenOperation.cc - operations/COM_KeyingScreenOperation.h - - nodes/COM_TrackPositionNode.cc - nodes/COM_TrackPositionNode.h - operations/COM_TrackPositionOperation.cc - operations/COM_TrackPositionOperation.h - - nodes/COM_KeyingNode.cc - nodes/COM_KeyingNode.h - operations/COM_KeyingBlurOperation.cc - operations/COM_KeyingBlurOperation.h - operations/COM_KeyingClipOperation.cc - operations/COM_KeyingClipOperation.h - operations/COM_KeyingDespillOperation.cc - operations/COM_KeyingDespillOperation.h - operations/COM_KeyingOperation.cc - operations/COM_KeyingOperation.h - - operations/COM_ColorSpillOperation.cc - operations/COM_ColorSpillOperation.h - operations/COM_RenderLayersProg.cc - operations/COM_RenderLayersProg.h - - operations/COM_BokehImageOperation.cc - operations/COM_BokehImageOperation.h - operations/COM_ImageOperation.cc - operations/COM_ImageOperation.h - operations/COM_MultilayerImageOperation.cc - operations/COM_MultilayerImageOperation.h - operations/COM_TextureOperation.cc - operations/COM_TextureOperation.h - - - operations/COM_SocketProxyOperation.cc - operations/COM_SocketProxyOperation.h - - operations/COM_CompositorOperation.cc - operations/COM_CompositorOperation.h - operations/COM_ConvertDepthToRadiusOperation.cc - operations/COM_ConvertDepthToRadiusOperation.h - operations/COM_OutputFileMultiViewOperation.cc - operations/COM_OutputFileMultiViewOperation.h - operations/COM_OutputFileOperation.cc - operations/COM_OutputFileOperation.h - operations/COM_PreviewOperation.cc - operations/COM_PreviewOperation.h - operations/COM_SplitOperation.cc - operations/COM_SplitOperation.h - operations/COM_ViewerOperation.cc - operations/COM_ViewerOperation.h - operations/COM_ZCombineOperation.cc - operations/COM_ZCombineOperation.h - - operations/COM_ChangeHSVOperation.cc - operations/COM_ChangeHSVOperation.h - operations/COM_ChannelMatteOperation.cc - operations/COM_ChannelMatteOperation.h - operations/COM_ChromaMatteOperation.cc - operations/COM_ChromaMatteOperation.h - operations/COM_ColorCurveOperation.cc - operations/COM_ColorCurveOperation.h - operations/COM_ColorExposureOperation.cc - operations/COM_ColorExposureOperation.h - operations/COM_ColorMatteOperation.cc - operations/COM_ColorMatteOperation.h - operations/COM_ColorRampOperation.cc - operations/COM_ColorRampOperation.h - operations/COM_CurveBaseOperation.cc - operations/COM_CurveBaseOperation.h - operations/COM_DifferenceMatteOperation.cc - operations/COM_DifferenceMatteOperation.h - operations/COM_DistanceRGBMatteOperation.cc - operations/COM_DistanceRGBMatteOperation.h - operations/COM_DistanceYCCMatteOperation.cc - operations/COM_DistanceYCCMatteOperation.h - operations/COM_HueSaturationValueCorrectOperation.cc - operations/COM_HueSaturationValueCorrectOperation.h - operations/COM_LuminanceMatteOperation.cc - operations/COM_LuminanceMatteOperation.h - operations/COM_VectorCurveOperation.cc - operations/COM_VectorCurveOperation.h - - operations/COM_BrightnessOperation.cc - operations/COM_BrightnessOperation.h - operations/COM_ColorCorrectionOperation.cc - operations/COM_ColorCorrectionOperation.h - operations/COM_ConstantOperation.cc - operations/COM_ConstantOperation.h - operations/COM_GammaOperation.cc - operations/COM_GammaOperation.h - operations/COM_MixOperation.cc - operations/COM_MixOperation.h - operations/COM_ReadBufferOperation.cc - operations/COM_ReadBufferOperation.h - operations/COM_SetColorOperation.cc - operations/COM_SetColorOperation.h - operations/COM_SetValueOperation.cc - operations/COM_SetValueOperation.h - operations/COM_SetVectorOperation.cc - operations/COM_SetVectorOperation.h - operations/COM_WriteBufferOperation.cc - operations/COM_WriteBufferOperation.h - - operations/COM_MathBaseOperation.cc - operations/COM_MathBaseOperation.h - - operations/COM_AlphaOverKeyOperation.cc - operations/COM_AlphaOverKeyOperation.h - operations/COM_AlphaOverMixedOperation.cc - operations/COM_AlphaOverMixedOperation.h - operations/COM_AlphaOverPremultiplyOperation.cc - operations/COM_AlphaOverPremultiplyOperation.h - - operations/COM_ColorBalanceASCCDLOperation.cc - operations/COM_ColorBalanceASCCDLOperation.h - operations/COM_ColorBalanceLGGOperation.cc - operations/COM_ColorBalanceLGGOperation.h - operations/COM_InvertOperation.cc - operations/COM_InvertOperation.h - operations/COM_MapRangeOperation.cc - operations/COM_MapRangeOperation.h - operations/COM_MapValueOperation.cc - operations/COM_MapValueOperation.h - operations/COM_SetAlphaMultiplyOperation.cc - operations/COM_SetAlphaMultiplyOperation.h - operations/COM_SetAlphaReplaceOperation.cc - operations/COM_SetAlphaReplaceOperation.h - - # Distort operation - operations/COM_DisplaceOperation.cc - operations/COM_DisplaceOperation.h - operations/COM_DisplaceSimpleOperation.cc - operations/COM_DisplaceSimpleOperation.h - operations/COM_FlipOperation.cc - operations/COM_FlipOperation.h - operations/COM_MapUVOperation.cc - operations/COM_MapUVOperation.h - operations/COM_PlaneCornerPinOperation.cc - operations/COM_PlaneCornerPinOperation.h - operations/COM_PlaneDistortCommonOperation.cc - operations/COM_PlaneDistortCommonOperation.h - operations/COM_PlaneTrackOperation.cc - operations/COM_PlaneTrackOperation.h - operations/COM_ProjectorLensDistortionOperation.cc - operations/COM_ProjectorLensDistortionOperation.h - operations/COM_RotateOperation.cc - operations/COM_RotateOperation.h - operations/COM_ScaleOperation.cc - operations/COM_ScaleOperation.h - operations/COM_ScreenLensDistortionOperation.cc - operations/COM_ScreenLensDistortionOperation.h - operations/COM_TransformOperation.cc - operations/COM_TransformOperation.h - operations/COM_TranslateOperation.cc - operations/COM_TranslateOperation.h - operations/COM_WrapOperation.cc - operations/COM_WrapOperation.h - - # Filter operations - operations/COM_ConvolutionEdgeFilterOperation.cc - operations/COM_ConvolutionEdgeFilterOperation.h - operations/COM_ConvolutionFilterOperation.cc - operations/COM_ConvolutionFilterOperation.h - operations/COM_DenoiseOperation.cc - operations/COM_DenoiseOperation.h - operations/COM_DespeckleOperation.cc - operations/COM_DespeckleOperation.h - operations/COM_DilateErodeOperation.cc - operations/COM_DilateErodeOperation.h - operations/COM_GlareBaseOperation.cc - operations/COM_GlareBaseOperation.h - operations/COM_GlareFogGlowOperation.cc - operations/COM_GlareFogGlowOperation.h - operations/COM_GlareGhostOperation.cc - operations/COM_GlareGhostOperation.h - operations/COM_GlareSimpleStarOperation.cc - operations/COM_GlareSimpleStarOperation.h - operations/COM_GlareStreaksOperation.cc - operations/COM_GlareStreaksOperation.h - operations/COM_GlareThresholdOperation.cc - operations/COM_GlareThresholdOperation.h - operations/COM_InpaintOperation.cc - operations/COM_InpaintOperation.h - operations/COM_SetSamplerOperation.cc - operations/COM_SetSamplerOperation.h - - - # Convert operations - operations/COM_ConvertOperation.cc - operations/COM_ConvertOperation.h - operations/COM_IDMaskOperation.cc - operations/COM_IDMaskOperation.h - - operations/COM_ConvertColorSpaceOperation.cc - operations/COM_ConvertColorSpaceOperation.h - operations/COM_DotproductOperation.cc - operations/COM_DotproductOperation.h - - # Matte operation - operations/COM_BoxMaskOperation.cc - operations/COM_BoxMaskOperation.h - operations/COM_EllipseMaskOperation.cc - operations/COM_EllipseMaskOperation.h - - operations/COM_ConvertColorProfileOperation.cc - operations/COM_ConvertColorProfileOperation.h - operations/COM_MovieClipOperation.cc - operations/COM_MovieClipOperation.h - - operations/COM_AntiAliasOperation.cc - operations/COM_AntiAliasOperation.h - - operations/COM_MaskOperation.cc - operations/COM_MaskOperation.h -) - -set(LIB - bf_blenkernel - bf_blenlib - extern_clew -) - -list(APPEND INC - ${CMAKE_CURRENT_BINARY_DIR}/operations -) - -data_to_c( - ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl - ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h - SRC -) - -add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) - -set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) -set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) -add_custom_command( - OUTPUT ${GENSRC} - COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} - COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} - DEPENDS smaa_areatex -) -add_custom_target(smaa_areatex_header - SOURCES ${GENSRC} -) -list(APPEND SRC - ${GENSRC} -) -unset(GENSRC) -unset(GENSRC_DIR) - -if(WITH_OPENIMAGEDENOISE) - add_definitions(-DWITH_OPENIMAGEDENOISE) - add_definitions(-DOIDN_STATIC_LIB) - list(APPEND INC_SYS - ${OPENIMAGEDENOISE_INCLUDE_DIRS} - ${TBB_INCLUDE_DIRS} +add_subdirectory(realtime_compositor) + +if(WITH_COMPOSITOR_CPU) + set(INC + . + intern + nodes + operations + ../blenkernel + ../blenlib + ../blentranslation + ../depsgraph + ../imbuf + ../makesdna + ../makesrna + ../nodes + ../windowmanager + ../nodes/composite + ../nodes/intern + ../render + ../render/intern + ../../../extern/clew/include + ../../../intern/atomic + ../../../intern/clog + ../../../intern/guardedalloc + + # dna_type_offsets.h + ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern + # RNA_prototypes.h + ${CMAKE_BINARY_DIR}/source/blender/makesrna ) - list(APPEND LIB - ${OPENIMAGEDENOISE_LIBRARIES} - ${TBB_LIBRARIES} + + set(INC_SYS + ) -endif() -blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + set(SRC + COM_compositor.h + COM_defines.h + + intern/COM_BufferArea.h + intern/COM_BufferOperation.cc + intern/COM_BufferOperation.h + intern/COM_BufferRange.h + intern/COM_BuffersIterator.h + intern/COM_CPUDevice.cc + intern/COM_CPUDevice.h + intern/COM_ChunkOrder.cc + intern/COM_ChunkOrder.h + intern/COM_ChunkOrderHotspot.cc + intern/COM_ChunkOrderHotspot.h + intern/COM_CompositorContext.cc + intern/COM_CompositorContext.h + intern/COM_ConstantFolder.cc + intern/COM_ConstantFolder.h + intern/COM_Converter.cc + intern/COM_Converter.h + intern/COM_Debug.cc + intern/COM_Debug.h + intern/COM_Device.cc + intern/COM_Device.h + intern/COM_Enums.cc + intern/COM_Enums.h + intern/COM_ExecutionGroup.cc + intern/COM_ExecutionGroup.h + intern/COM_ExecutionModel.cc + intern/COM_ExecutionModel.h + intern/COM_ExecutionSystem.cc + intern/COM_ExecutionSystem.h + intern/COM_FullFrameExecutionModel.cc + intern/COM_FullFrameExecutionModel.h + intern/COM_MemoryBuffer.cc + intern/COM_MemoryBuffer.h + intern/COM_MemoryProxy.cc + intern/COM_MemoryProxy.h + intern/COM_MetaData.cc + intern/COM_MetaData.h + intern/COM_MultiThreadedOperation.cc + intern/COM_MultiThreadedOperation.h + intern/COM_MultiThreadedRowOperation.cc + intern/COM_MultiThreadedRowOperation.h + intern/COM_Node.cc + intern/COM_Node.h + intern/COM_NodeConverter.cc + intern/COM_NodeConverter.h + intern/COM_NodeGraph.cc + intern/COM_NodeGraph.h + intern/COM_NodeOperation.cc + intern/COM_NodeOperation.h + intern/COM_NodeOperationBuilder.cc + intern/COM_NodeOperationBuilder.h + intern/COM_OpenCLDevice.cc + intern/COM_OpenCLDevice.h + intern/COM_SharedOperationBuffers.cc + intern/COM_SharedOperationBuffers.h + intern/COM_SingleThreadedOperation.cc + intern/COM_SingleThreadedOperation.h + intern/COM_TiledExecutionModel.cc + intern/COM_TiledExecutionModel.h + intern/COM_WorkPackage.cc + intern/COM_WorkPackage.h + intern/COM_WorkScheduler.cc + intern/COM_WorkScheduler.h + intern/COM_compositor.cc + + operations/COM_QualityStepHelper.cc + operations/COM_QualityStepHelper.h + + # Internal nodes + nodes/COM_SocketProxyNode.cc + nodes/COM_SocketProxyNode.h + + # input nodes + nodes/COM_BokehImageNode.cc + nodes/COM_BokehImageNode.h + nodes/COM_ColorNode.cc + nodes/COM_ColorNode.h + nodes/COM_ImageNode.cc + nodes/COM_ImageNode.h + nodes/COM_MaskNode.cc + nodes/COM_MaskNode.h + nodes/COM_MovieClipNode.cc + nodes/COM_MovieClipNode.h + nodes/COM_OutputFileNode.cc + nodes/COM_OutputFileNode.h + nodes/COM_RenderLayersNode.cc + nodes/COM_RenderLayersNode.h + nodes/COM_SceneTimeNode.cc + nodes/COM_SceneTimeNode.h + nodes/COM_SwitchNode.cc + nodes/COM_SwitchNode.h + nodes/COM_SwitchViewNode.cc + nodes/COM_SwitchViewNode.h + nodes/COM_TextureNode.cc + nodes/COM_TextureNode.h + nodes/COM_TimeNode.cc + nodes/COM_TimeNode.h + nodes/COM_ValueNode.cc + nodes/COM_ValueNode.h + + # output nodes + nodes/COM_CompositorNode.cc + nodes/COM_CompositorNode.h + nodes/COM_SplitViewerNode.cc + nodes/COM_SplitViewerNode.h + nodes/COM_ViewLevelsNode.cc + nodes/COM_ViewLevelsNode.h + nodes/COM_ViewerNode.cc + nodes/COM_ViewerNode.h + operations/COM_CalculateMeanOperation.cc + operations/COM_CalculateMeanOperation.h + operations/COM_CalculateStandardDeviationOperation.cc + operations/COM_CalculateStandardDeviationOperation.h + + # distort nodes + nodes/COM_FlipNode.cc + nodes/COM_FlipNode.h + nodes/COM_RotateNode.cc + nodes/COM_RotateNode.h + nodes/COM_ScaleNode.cc + nodes/COM_ScaleNode.h + nodes/COM_TranslateNode.cc + nodes/COM_TranslateNode.h + + nodes/COM_DisplaceNode.cc + nodes/COM_DisplaceNode.h + nodes/COM_MapUVNode.cc + nodes/COM_MapUVNode.h + + nodes/COM_ChannelMatteNode.cc + nodes/COM_ChannelMatteNode.h + nodes/COM_ChromaMatteNode.cc + nodes/COM_ChromaMatteNode.h + nodes/COM_ColorMatteNode.cc + nodes/COM_ColorMatteNode.h + nodes/COM_DifferenceMatteNode.cc + nodes/COM_DifferenceMatteNode.h + nodes/COM_DistanceMatteNode.cc + nodes/COM_DistanceMatteNode.h + nodes/COM_LensDistortionNode.cc + nodes/COM_LensDistortionNode.h + nodes/COM_LuminanceMatteNode.cc + nodes/COM_LuminanceMatteNode.h + + nodes/COM_GlareNode.cc + nodes/COM_GlareNode.h + + nodes/COM_SunBeamsNode.cc + nodes/COM_SunBeamsNode.h + operations/COM_SunBeamsOperation.cc + operations/COM_SunBeamsOperation.h + + nodes/COM_CryptomatteNode.cc + nodes/COM_CryptomatteNode.h + operations/COM_CryptomatteOperation.cc + operations/COM_CryptomatteOperation.h + + nodes/COM_CornerPinNode.cc + nodes/COM_CornerPinNode.h + nodes/COM_PlaneTrackDeformNode.cc + nodes/COM_PlaneTrackDeformNode.h + + nodes/COM_CropNode.cc + nodes/COM_CropNode.h + operations/COM_CropOperation.cc + operations/COM_CropOperation.h + + nodes/COM_DefocusNode.cc + nodes/COM_DefocusNode.h + nodes/COM_MovieDistortionNode.cc + nodes/COM_MovieDistortionNode.h + nodes/COM_Stabilize2dNode.cc + nodes/COM_Stabilize2dNode.h + nodes/COM_TransformNode.cc + nodes/COM_TransformNode.h + + # color nodes + nodes/COM_AlphaOverNode.cc + nodes/COM_AlphaOverNode.h + nodes/COM_BrightnessNode.cc + nodes/COM_BrightnessNode.h + nodes/COM_ColorBalanceNode.cc + nodes/COM_ColorBalanceNode.h + nodes/COM_ColorCorrectionNode.cc + nodes/COM_ColorCorrectionNode.h + nodes/COM_ColorCurveNode.cc + nodes/COM_ColorCurveNode.h + nodes/COM_ColorExposureNode.cc + nodes/COM_ColorExposureNode.h + nodes/COM_ColorRampNode.cc + nodes/COM_ColorRampNode.h + nodes/COM_ColorToBWNode.cc + nodes/COM_ColorToBWNode.h + nodes/COM_ConvertAlphaNode.cc + nodes/COM_ConvertAlphaNode.h + nodes/COM_ConvertColorSpaceNode.cc + nodes/COM_ConvertColorSpaceNode.h + nodes/COM_GammaNode.cc + nodes/COM_GammaNode.h + nodes/COM_HueSaturationValueCorrectNode.cc + nodes/COM_HueSaturationValueCorrectNode.h + nodes/COM_HueSaturationValueNode.cc + nodes/COM_HueSaturationValueNode.h + nodes/COM_InvertNode.cc + nodes/COM_InvertNode.h + nodes/COM_MixNode.cc + nodes/COM_MixNode.h + nodes/COM_SetAlphaNode.cc + nodes/COM_SetAlphaNode.h + nodes/COM_TonemapNode.cc + nodes/COM_TonemapNode.h + nodes/COM_VectorCurveNode.cc + nodes/COM_VectorCurveNode.h + nodes/COM_ZCombineNode.cc + nodes/COM_ZCombineNode.h + operations/COM_TonemapOperation.cc + operations/COM_TonemapOperation.h + + # converter nodes + nodes/COM_CombineColorNode.cc + nodes/COM_CombineColorNode.h + nodes/COM_CombineColorNodeLegacy.cc + nodes/COM_CombineColorNodeLegacy.h + nodes/COM_CombineXYZNode.cc + nodes/COM_CombineXYZNode.h + nodes/COM_IDMaskNode.cc + nodes/COM_IDMaskNode.h + nodes/COM_SeparateColorNode.cc + nodes/COM_SeparateColorNode.h + nodes/COM_SeparateColorNodeLegacy.cc + nodes/COM_SeparateColorNodeLegacy.h + nodes/COM_SeparateXYZNode.cc + nodes/COM_SeparateXYZNode.h + + nodes/COM_MapRangeNode.cc + nodes/COM_MapRangeNode.h + nodes/COM_MapValueNode.cc + nodes/COM_MapValueNode.h + nodes/COM_MathNode.cc + nodes/COM_MathNode.h + nodes/COM_NormalNode.cc + nodes/COM_NormalNode.h + nodes/COM_NormalizeNode.cc + nodes/COM_NormalizeNode.h + + operations/COM_NormalizeOperation.cc + operations/COM_NormalizeOperation.h + + nodes/COM_PixelateNode.cc + nodes/COM_PixelateNode.h + operations/COM_PixelateOperation.cc + operations/COM_PixelateOperation.h + + # Filter nodes + nodes/COM_BilateralBlurNode.cc + nodes/COM_BilateralBlurNode.h + operations/COM_BilateralBlurOperation.cc + operations/COM_BilateralBlurOperation.h + nodes/COM_VectorBlurNode.cc + nodes/COM_VectorBlurNode.h + operations/COM_VectorBlurOperation.cc + operations/COM_VectorBlurOperation.h + nodes/COM_AntiAliasingNode.cc + nodes/COM_AntiAliasingNode.h + nodes/COM_BlurNode.cc + nodes/COM_BlurNode.h + nodes/COM_BokehBlurNode.cc + nodes/COM_BokehBlurNode.h + nodes/COM_DenoiseNode.cc + nodes/COM_DenoiseNode.h + nodes/COM_DespeckleNode.cc + nodes/COM_DespeckleNode.h + nodes/COM_DilateErodeNode.cc + nodes/COM_DilateErodeNode.h + nodes/COM_DirectionalBlurNode.cc + nodes/COM_DirectionalBlurNode.h + nodes/COM_FilterNode.cc + nodes/COM_FilterNode.h + nodes/COM_InpaintNode.cc + nodes/COM_InpaintNode.h + nodes/COM_PosterizeNode.cc + nodes/COM_PosterizeNode.h + + operations/COM_BlurBaseOperation.cc + operations/COM_BlurBaseOperation.h + operations/COM_BokehBlurOperation.cc + operations/COM_BokehBlurOperation.h + operations/COM_DirectionalBlurOperation.cc + operations/COM_DirectionalBlurOperation.h + operations/COM_FastGaussianBlurOperation.cc + operations/COM_FastGaussianBlurOperation.h + operations/COM_GammaCorrectOperation.cc + operations/COM_GammaCorrectOperation.h + operations/COM_GaussianAlphaBlurBaseOperation.cc + operations/COM_GaussianAlphaBlurBaseOperation.h + operations/COM_GaussianAlphaXBlurOperation.cc + operations/COM_GaussianAlphaXBlurOperation.h + operations/COM_GaussianAlphaYBlurOperation.cc + operations/COM_GaussianAlphaYBlurOperation.h + operations/COM_GaussianBlurBaseOperation.cc + operations/COM_GaussianBlurBaseOperation.h + operations/COM_GaussianBokehBlurOperation.cc + operations/COM_GaussianBokehBlurOperation.h + operations/COM_GaussianXBlurOperation.cc + operations/COM_GaussianXBlurOperation.h + operations/COM_GaussianYBlurOperation.cc + operations/COM_GaussianYBlurOperation.h + operations/COM_MovieClipAttributeOperation.cc + operations/COM_MovieClipAttributeOperation.h + operations/COM_MovieDistortionOperation.cc + operations/COM_MovieDistortionOperation.h + operations/COM_PosterizeOperation.cc + operations/COM_PosterizeOperation.h + operations/COM_SMAAOperation.cc + operations/COM_SMAAOperation.h + operations/COM_VariableSizeBokehBlurOperation.cc + operations/COM_VariableSizeBokehBlurOperation.h + + # Matte nodes + nodes/COM_BoxMaskNode.cc + nodes/COM_BoxMaskNode.h + nodes/COM_ColorSpillNode.cc + nodes/COM_ColorSpillNode.h + nodes/COM_DoubleEdgeMaskNode.cc + nodes/COM_DoubleEdgeMaskNode.h + nodes/COM_EllipseMaskNode.cc + nodes/COM_EllipseMaskNode.h + + operations/COM_DoubleEdgeMaskOperation.cc + operations/COM_DoubleEdgeMaskOperation.h + + + nodes/COM_KeyingScreenNode.cc + nodes/COM_KeyingScreenNode.h + operations/COM_KeyingScreenOperation.cc + operations/COM_KeyingScreenOperation.h + + nodes/COM_TrackPositionNode.cc + nodes/COM_TrackPositionNode.h + operations/COM_TrackPositionOperation.cc + operations/COM_TrackPositionOperation.h + + nodes/COM_KeyingNode.cc + nodes/COM_KeyingNode.h + operations/COM_KeyingBlurOperation.cc + operations/COM_KeyingBlurOperation.h + operations/COM_KeyingClipOperation.cc + operations/COM_KeyingClipOperation.h + operations/COM_KeyingDespillOperation.cc + operations/COM_KeyingDespillOperation.h + operations/COM_KeyingOperation.cc + operations/COM_KeyingOperation.h + + operations/COM_ColorSpillOperation.cc + operations/COM_ColorSpillOperation.h + operations/COM_RenderLayersProg.cc + operations/COM_RenderLayersProg.h + + operations/COM_BokehImageOperation.cc + operations/COM_BokehImageOperation.h + operations/COM_ImageOperation.cc + operations/COM_ImageOperation.h + operations/COM_MultilayerImageOperation.cc + operations/COM_MultilayerImageOperation.h + operations/COM_TextureOperation.cc + operations/COM_TextureOperation.h + + + operations/COM_SocketProxyOperation.cc + operations/COM_SocketProxyOperation.h + + operations/COM_CompositorOperation.cc + operations/COM_CompositorOperation.h + operations/COM_ConvertDepthToRadiusOperation.cc + operations/COM_ConvertDepthToRadiusOperation.h + operations/COM_OutputFileMultiViewOperation.cc + operations/COM_OutputFileMultiViewOperation.h + operations/COM_OutputFileOperation.cc + operations/COM_OutputFileOperation.h + operations/COM_PreviewOperation.cc + operations/COM_PreviewOperation.h + operations/COM_SplitOperation.cc + operations/COM_SplitOperation.h + operations/COM_ViewerOperation.cc + operations/COM_ViewerOperation.h + operations/COM_ZCombineOperation.cc + operations/COM_ZCombineOperation.h + + operations/COM_ChangeHSVOperation.cc + operations/COM_ChangeHSVOperation.h + operations/COM_ChannelMatteOperation.cc + operations/COM_ChannelMatteOperation.h + operations/COM_ChromaMatteOperation.cc + operations/COM_ChromaMatteOperation.h + operations/COM_ColorCurveOperation.cc + operations/COM_ColorCurveOperation.h + operations/COM_ColorExposureOperation.cc + operations/COM_ColorExposureOperation.h + operations/COM_ColorMatteOperation.cc + operations/COM_ColorMatteOperation.h + operations/COM_ColorRampOperation.cc + operations/COM_ColorRampOperation.h + operations/COM_CurveBaseOperation.cc + operations/COM_CurveBaseOperation.h + operations/COM_DifferenceMatteOperation.cc + operations/COM_DifferenceMatteOperation.h + operations/COM_DistanceRGBMatteOperation.cc + operations/COM_DistanceRGBMatteOperation.h + operations/COM_DistanceYCCMatteOperation.cc + operations/COM_DistanceYCCMatteOperation.h + operations/COM_HueSaturationValueCorrectOperation.cc + operations/COM_HueSaturationValueCorrectOperation.h + operations/COM_LuminanceMatteOperation.cc + operations/COM_LuminanceMatteOperation.h + operations/COM_VectorCurveOperation.cc + operations/COM_VectorCurveOperation.h + + operations/COM_BrightnessOperation.cc + operations/COM_BrightnessOperation.h + operations/COM_ColorCorrectionOperation.cc + operations/COM_ColorCorrectionOperation.h + operations/COM_ConstantOperation.cc + operations/COM_ConstantOperation.h + operations/COM_GammaOperation.cc + operations/COM_GammaOperation.h + operations/COM_MixOperation.cc + operations/COM_MixOperation.h + operations/COM_ReadBufferOperation.cc + operations/COM_ReadBufferOperation.h + operations/COM_SetColorOperation.cc + operations/COM_SetColorOperation.h + operations/COM_SetValueOperation.cc + operations/COM_SetValueOperation.h + operations/COM_SetVectorOperation.cc + operations/COM_SetVectorOperation.h + operations/COM_WriteBufferOperation.cc + operations/COM_WriteBufferOperation.h + + operations/COM_MathBaseOperation.cc + operations/COM_MathBaseOperation.h + + operations/COM_AlphaOverKeyOperation.cc + operations/COM_AlphaOverKeyOperation.h + operations/COM_AlphaOverMixedOperation.cc + operations/COM_AlphaOverMixedOperation.h + operations/COM_AlphaOverPremultiplyOperation.cc + operations/COM_AlphaOverPremultiplyOperation.h + + operations/COM_ColorBalanceASCCDLOperation.cc + operations/COM_ColorBalanceASCCDLOperation.h + operations/COM_ColorBalanceLGGOperation.cc + operations/COM_ColorBalanceLGGOperation.h + operations/COM_InvertOperation.cc + operations/COM_InvertOperation.h + operations/COM_MapRangeOperation.cc + operations/COM_MapRangeOperation.h + operations/COM_MapValueOperation.cc + operations/COM_MapValueOperation.h + operations/COM_SetAlphaMultiplyOperation.cc + operations/COM_SetAlphaMultiplyOperation.h + operations/COM_SetAlphaReplaceOperation.cc + operations/COM_SetAlphaReplaceOperation.h + + # Distort operation + operations/COM_DisplaceOperation.cc + operations/COM_DisplaceOperation.h + operations/COM_DisplaceSimpleOperation.cc + operations/COM_DisplaceSimpleOperation.h + operations/COM_FlipOperation.cc + operations/COM_FlipOperation.h + operations/COM_MapUVOperation.cc + operations/COM_MapUVOperation.h + operations/COM_PlaneCornerPinOperation.cc + operations/COM_PlaneCornerPinOperation.h + operations/COM_PlaneDistortCommonOperation.cc + operations/COM_PlaneDistortCommonOperation.h + operations/COM_PlaneTrackOperation.cc + operations/COM_PlaneTrackOperation.h + operations/COM_ProjectorLensDistortionOperation.cc + operations/COM_ProjectorLensDistortionOperation.h + operations/COM_RotateOperation.cc + operations/COM_RotateOperation.h + operations/COM_ScaleOperation.cc + operations/COM_ScaleOperation.h + operations/COM_ScreenLensDistortionOperation.cc + operations/COM_ScreenLensDistortionOperation.h + operations/COM_TransformOperation.cc + operations/COM_TransformOperation.h + operations/COM_TranslateOperation.cc + operations/COM_TranslateOperation.h + operations/COM_WrapOperation.cc + operations/COM_WrapOperation.h + + # Filter operations + operations/COM_ConvolutionEdgeFilterOperation.cc + operations/COM_ConvolutionEdgeFilterOperation.h + operations/COM_ConvolutionFilterOperation.cc + operations/COM_ConvolutionFilterOperation.h + operations/COM_DenoiseOperation.cc + operations/COM_DenoiseOperation.h + operations/COM_DespeckleOperation.cc + operations/COM_DespeckleOperation.h + operations/COM_DilateErodeOperation.cc + operations/COM_DilateErodeOperation.h + operations/COM_GlareBaseOperation.cc + operations/COM_GlareBaseOperation.h + operations/COM_GlareFogGlowOperation.cc + operations/COM_GlareFogGlowOperation.h + operations/COM_GlareGhostOperation.cc + operations/COM_GlareGhostOperation.h + operations/COM_GlareSimpleStarOperation.cc + operations/COM_GlareSimpleStarOperation.h + operations/COM_GlareStreaksOperation.cc + operations/COM_GlareStreaksOperation.h + operations/COM_GlareThresholdOperation.cc + operations/COM_GlareThresholdOperation.h + operations/COM_InpaintOperation.cc + operations/COM_InpaintOperation.h + operations/COM_SetSamplerOperation.cc + operations/COM_SetSamplerOperation.h + + + # Convert operations + operations/COM_ConvertOperation.cc + operations/COM_ConvertOperation.h + operations/COM_IDMaskOperation.cc + operations/COM_IDMaskOperation.h + + operations/COM_ConvertColorSpaceOperation.cc + operations/COM_ConvertColorSpaceOperation.h + operations/COM_DotproductOperation.cc + operations/COM_DotproductOperation.h + + # Matte operation + operations/COM_BoxMaskOperation.cc + operations/COM_BoxMaskOperation.h + operations/COM_EllipseMaskOperation.cc + operations/COM_EllipseMaskOperation.h + + operations/COM_ConvertColorProfileOperation.cc + operations/COM_ConvertColorProfileOperation.h + operations/COM_MovieClipOperation.cc + operations/COM_MovieClipOperation.h + + operations/COM_AntiAliasOperation.cc + operations/COM_AntiAliasOperation.h + + operations/COM_MaskOperation.cc + operations/COM_MaskOperation.h + ) -if(WITH_UNITY_BUILD) - set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON) - set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10) -endif() + set(LIB + bf_blenkernel + bf_blenlib + extern_clew + ) -if(COMMAND target_precompile_headers) - target_precompile_headers(bf_compositor PRIVATE COM_precomp.h) -endif() + list(APPEND INC + ${CMAKE_CURRENT_BINARY_DIR}/operations + ) -if(CXX_WARN_NO_SUGGEST_OVERRIDE) - target_compile_options(bf_compositor PRIVATE "-Wsuggest-override") -endif() + data_to_c( + ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl + ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h + SRC + ) -add_dependencies(bf_compositor smaa_areatex_header) + add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) -if(WITH_GTESTS) - set(TEST_SRC - tests/COM_BufferArea_test.cc - tests/COM_BufferRange_test.cc - tests/COM_BuffersIterator_test.cc - tests/COM_NodeOperation_test.cc + set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) + set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) + add_custom_command( + OUTPUT ${GENSRC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} + COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} + DEPENDS smaa_areatex ) - set(TEST_INC + add_custom_target(smaa_areatex_header + SOURCES ${GENSRC} ) - set(TEST_LIB - bf_compositor + list(APPEND SRC + ${GENSRC} ) - include(GTestTesting) - blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") -endif() + unset(GENSRC) + unset(GENSRC_DIR) + + if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) + add_definitions(-DOIDN_STATIC_LIB) + list(APPEND INC_SYS + ${OPENIMAGEDENOISE_INCLUDE_DIRS} + ${TBB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENIMAGEDENOISE_LIBRARIES} + ${TBB_LIBRARIES} + ) + endif() + + blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + + if(WITH_UNITY_BUILD) + set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON) + set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10) + endif() + + if(COMMAND target_precompile_headers) + target_precompile_headers(bf_compositor PRIVATE COM_precomp.h) + endif() + + if(CXX_WARN_NO_SUGGEST_OVERRIDE) + target_compile_options(bf_compositor PRIVATE "-Wsuggest-override") + endif() + + add_dependencies(bf_compositor smaa_areatex_header) + + if(WITH_GTESTS) + set(TEST_SRC + tests/COM_BufferArea_test.cc + tests/COM_BufferRange_test.cc + tests/COM_BuffersIterator_test.cc + tests/COM_NodeOperation_test.cc + ) + set(TEST_INC + ) + set(TEST_LIB + bf_compositor + ) + include(GTestTesting) + blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") + endif() + + # Needed so we can use dna_type_offsets.h for defaults initialization. + add_dependencies(bf_compositor bf_dna) + # RNA_prototypes.h + add_dependencies(bf_compositor bf_rna) -# Needed so we can use dna_type_offsets.h for defaults initialization. -add_dependencies(bf_compositor bf_dna) -# RNA_prototypes.h -add_dependencies(bf_compositor bf_rna) +# End WITH_COMPOSITOR_CPU. +endif() diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc index f69511d88e6..a62d21bb657 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cc +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc @@ -37,6 +37,10 @@ void OutputFileNode::map_input_sockets(NodeConverter &converter, void OutputFileNode::add_preview_to_first_linked_input(NodeConverter &converter) const { + if (get_input_sockets().is_empty()) { + return; + } + NodeInput *first_socket = this->get_input_socket(0); if (first_socket->is_linked()) { converter.add_node_input_preview(first_socket); diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index d7cf41cf422..b62d972e807 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -74,7 +74,7 @@ void MovieClipBaseOperation::execute_pixel_sampled(float output[4], zero_v4(output); } else if (ibuf->rect == nullptr && ibuf->rect_float == nullptr) { - /* Happens for multilayer exr, i.e. */ + /* Happens for multi-layer EXR, i.e. */ zero_v4(output); } else { diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 1957c5eb5fc..2a2aff31893 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -7,7 +7,7 @@ namespace blender::compositor { #define USE_FORCE_BILINEAR -/* XXX(campbell): ignore input and use default from old compositor, +/* XXX(@campbellbarton): ignore input and use default from old compositor, * could become an option like the transform node. * * NOTE: use bilinear because bicubic makes fuzzy even when not scaling at all (1:1) diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt new file mode 100644 index 00000000000..9fe156c3ef2 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +set(INC + . + ../../gpu + ../../nodes + ../../imbuf + ../../blenlib + ../../makesdna + ../../makesrna + ../../blenkernel + ../../gpu/intern + ../../../../intern/guardedalloc +) + + +set(SRC + intern/compile_state.cc + intern/context.cc + intern/conversion_operation.cc + intern/domain.cc + intern/evaluator.cc + intern/input_single_value_operation.cc + intern/node_operation.cc + intern/operation.cc + intern/realize_on_domain_operation.cc + intern/reduce_to_single_value_operation.cc + intern/result.cc + intern/scheduler.cc + intern/shader_node.cc + intern/shader_operation.cc + intern/simple_operation.cc + intern/static_shader_manager.cc + intern/texture_pool.cc + intern/utilities.cc + + COM_compile_state.hh + COM_context.hh + COM_conversion_operation.hh + COM_domain.hh + COM_evaluator.hh + COM_input_descriptor.hh + COM_input_single_value_operation.hh + COM_node_operation.hh + COM_operation.hh + COM_realize_on_domain_operation.hh + COM_reduce_to_single_value_operation.hh + COM_result.hh + COM_scheduler.hh + COM_shader_node.hh + COM_shader_operation.hh + COM_simple_operation.hh + COM_static_shader_manager.hh + COM_texture_pool.hh + COM_utilities.hh +) + +set(LIB + bf_gpu + bf_nodes + bf_imbuf + bf_blenlib + bf_blenkernel +) + +blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/compositor/realtime_compositor/COM_compile_state.hh b/source/blender/compositor/realtime_compositor/COM_compile_state.hh new file mode 100644 index 00000000000..ed6ad414e3b --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_compile_state.hh @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_map.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_domain.hh" +#include "COM_node_operation.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Compile State + * + * The compile state is a utility class used to track the state of compilation when compiling the + * node tree. In particular, it tracks two important pieces of information, each of which is + * described in one of the following sections. + * + * First, it stores a mapping between all nodes and the operations they were compiled into. The + * mapping are stored independently depending on the type of the operation in the node_operations_ + * and shader_operations_ maps. So those two maps are mutually exclusive. The compiler should call + * the map_node_to_node_operation and map_node_to_shader_operation methods to populate those maps + * as soon as it compiles a node or multiple nodes into an operation. Those maps are used to + * retrieve the results of outputs linked to the inputs of operations. For more details, see the + * get_result_from_output_socket method. For the node tree shown below, nodes 1, 2, and 6 are + * mapped to their compiled operations in the node_operation_ map. While nodes 3 and 4 are both + * mapped to the first shader operation, and node 5 is mapped to the second shader operation in the + * shader_operations_ map. + * + * Shader Operation 1 Shader Operation 2 + * +-----------------------------------+ +------------------+ + * .------------. | .------------. .------------. | | .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 | + * | |----|--| |--| |---|-----|--| |--|--| | + * | | .-|--| | | | | .--|--| | | | | + * '------------' | | '------------' '------------' | | | '------------' | '------------' + * | +-----------------------------------+ | +------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'----------------------------------------' + * | | + * '------------' + * + * Second, it stores the shader compile unit as well as its domain. One should first go over the + * discussion in COM_evaluator.hh for a high level description of the mechanism of the compile + * unit. The one important detail in this class is the should_compile_shader_compile_unit method, + * which implements the criteria of whether the compile unit should be compiled given the node + * currently being processed as an argument. Those criteria are described as follows. If the + * compile unit is empty as is the case when processing nodes 1, 2, and 3, then it plainly + * shouldn't be compiled. If the given node is not a shader node, then it can't be added to the + * compile unit and the unit is considered complete and should be compiled, as is the case when + * processing node 6. If the computed domain of the given node is not compatible with the domain of + * the compiled unit, then it can't be added to the unit and the unit is considered complete and + * should be compiled, as is the case when processing node 5, more on this in the next section. + * Otherwise, the given node is compatible with the compile unit and can be added to it, so the + * unit shouldn't be compiled just yet, as is the case when processing node 4. + * + * Special attention should be given to the aforementioned domain compatibility criterion. One + * should first go over the discussion in COM_domain.hh for more information on domains. When a + * compile unit gets eventually compiled to a shader operation, that operation will have a certain + * operation domain, and any node that gets added to the compile unit should itself have a computed + * node domain that is compatible with that operation domain, otherwise, had the node been compiled + * into its own operation separately, the result would have been be different. For instance, + * consider the above node tree where node 1 outputs a 100x100 result, node 2 outputs a 50x50 + * result, the first input in node 3 has the highest domain priority, and the second input in node + * 5 has the highest domain priority. In this case, shader operation 1 will output a 100x100 + * result, and shader operation 2 will output a 50x50 result, because that's the computed operation + * domain for each of them. So node 6 will get a 50x50 result. Now consider the same node tree, but + * where all three nodes 3, 4, and 5 were compiled into a single shader operation as shown the node + * tree below. In that case, shader operation 1 will output a 100x100 result, because that's its + * computed operation domain. So node 6 will get a 100x100 result. As can be seen, the final result + * is different even though the node tree is the same. That's why the compiler can decide to + * compile the compile unit early even though further nodes can still be technically added to it. + * + * Shader Operation 1 + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * To check for the domain compatibility between the compile unit and the node being processed, + * the domain of the compile unit is assumed to be the domain of the first node whose computed + * domain is not an identity domain. Identity domains corresponds to single value results, so those + * are always compatible with any domain. The domain of the compile unit is computed and set in + * the add_node_to_shader_compile_unit method. When processing a node, the computed domain of node + * is compared to the compile unit domain in the should_compile_shader_compile_unit method, noting + * that identity domains are always compatible. Node domains are computed in the + * compute_shader_node_domain method, which is analogous to Operation::compute_domain for nodes + * that are not yet compiled. */ +class CompileState { + private: + /* A reference to the node execution schedule that is being compiled. */ + const Schedule &schedule_; + /* Those two maps associate each node with the operation it was compiled into. Each node is + * either compiled into a node operation and added to node_operations, or compiled into a shader + * operation and added to shader_operations. Those maps are used to retrieve the results of + * outputs linked to the inputs of operations. See the get_result_from_output_socket method for + * more information. */ + Map<DNode, NodeOperation *> node_operations_; + Map<DNode, ShaderOperation *> shader_operations_; + /* A contiguous subset of the node execution schedule that contains the group of nodes that will + * be compiled together into a Shader Operation. See the discussion in COM_evaluator.hh for + * more information. */ + ShaderCompileUnit shader_compile_unit_; + /* The domain of the shader compile unit. */ + Domain shader_compile_unit_domain_ = Domain::identity(); + + public: + /* Construct a compile state from the node execution schedule being compiled. */ + CompileState(const Schedule &schedule); + + /* Get a reference to the node execution schedule being compiled. */ + const Schedule &get_schedule(); + + /* Add an association between the given node and the give node operation that the node was + * compiled into in the node_operations_ map. */ + void map_node_to_node_operation(DNode node, NodeOperation *operation); + + /* Add an association between the given node and the give shader operation that the node was + * compiled into in the shader_operations_ map. */ + void map_node_to_shader_operation(DNode node, ShaderOperation *operation); + + /* Returns a reference to the result of the operation corresponding to the given output that the + * given output's node was compiled to. */ + Result &get_result_from_output_socket(DOutputSocket output); + + /* Add the given node to the compile unit. And if the domain of the compile unit is not yet + * determined or was determined to be an identity domain, update it to the computed domain for + * the give node. */ + void add_node_to_shader_compile_unit(DNode node); + + /* Get a reference to the shader compile unit. */ + ShaderCompileUnit &get_shader_compile_unit(); + + /* Clear the compile unit. This should be called once the compile unit is compiled to ready it to + * track the next potential compile unit. */ + void reset_shader_compile_unit(); + + /* Determines if the compile unit should be compiled based on a number of criteria give the node + * currently being processed. Those criteria are as follows: + * - If compile unit is empty, then it can't and shouldn't be compiled. + * - If the given node is not a shader node, then it can't be added to the compile unit + * and the unit is considered complete and should be compiled. + * - If the computed domain of the given node is not compatible with the domain of the compile + * unit, then it can't be added to it and the unit is considered complete and should be + * compiled. */ + bool should_compile_shader_compile_unit(DNode node); + + private: + /* Compute the node domain of the given shader node. This is analogous to the + * Operation::compute_domain method, except it is computed from the node itself as opposed to a + * compiled operation. See the discussion in COM_domain.hh for more information. */ + Domain compute_shader_node_domain(DNode node); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh new file mode 100644 index 00000000000..b5c8cea641f --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_context.hh @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" + +#include "DNA_scene_types.h" + +#include "GPU_texture.h" + +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Context + * + * A Context is an abstract class that is implemented by the caller of the evaluator to provide the + * necessary data and functionalities for the correct operation of the evaluator. This includes + * providing input data like render passes and the active scene, as well as references to the data + * where the output of the evaluator will be written. The class also provides a reference to the + * texture pool which should be implemented by the caller and provided during construction. + * Finally, the class have an instance of a static shader manager for convenient shader + * acquisition. */ +class Context { + private: + /* A texture pool that can be used to allocate textures for the compositor efficiently. */ + TexturePool &texture_pool_; + /* A static shader manager that can be used to acquire shaders for the compositor efficiently. */ + StaticShaderManager shader_manager_; + + public: + Context(TexturePool &texture_pool); + + /* Get the active compositing scene. */ + virtual const Scene *get_scene() const = 0; + + /* Get the dimensions of the output. */ + virtual int2 get_output_size() = 0; + + /* Get the texture representing the output where the result of the compositor should be + * written. This should be called by output nodes to get their target texture. */ + virtual GPUTexture *get_output_texture() = 0; + + /* Get the texture where the given render pass is stored. This should be called by the Render + * Layer node to populate its outputs. */ + virtual GPUTexture *get_input_texture(int view_layer, eScenePassType pass_type) = 0; + + /* Get the name of the view currently being rendered. */ + virtual StringRef get_view_name() = 0; + + /* Set an info message. This is called by the compositor evaluator to inform or warn the user + * about something, typically an error. The implementation should display the message in an + * appropriate place, which can be directly in the UI or just logged to the output stream. */ + virtual void set_info_message(StringRef message) const = 0; + + /* Get the current frame number of the active scene. */ + int get_frame_number() const; + + /* Get the current time in seconds of the active scene. */ + float get_time() const; + + /* Get a reference to the texture pool of this context. */ + TexturePool &texture_pool(); + + /* Get a reference to the static shader manager of this context. */ + StaticShaderManager &shader_manager(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh new file mode 100644 index 00000000000..15e1d0722ea --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Conversion Operation + * + * A simple operation that converts a result from a certain type to another. See the derived + * classes for more details. */ +class ConversionOperation : public SimpleOperation { + public: + using SimpleOperation::SimpleOperation; + + /* If the input result is a single value, execute_single is called. Otherwise, the shader + * provided by get_conversion_shader is dispatched. */ + void execute() override; + + /* Determine if a conversion operation is needed for the input with the given result and + * descriptor. If it is not needed, return a null pointer. If it is needed, return an instance of + * the appropriate conversion operation. */ + static SimpleOperation *construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor); + + protected: + /* Convert the input single value result to the output single value result. */ + virtual void execute_single(const Result &input, Result &output) = 0; + + /* Get the shader the will be used for conversion. */ + virtual GPUShader *get_conversion_shader() const = 0; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Float To Vector Operation + * + * Takes a float result and outputs a vector result. All three components of the output are filled + * with the input float. */ +class ConvertFloatToVectorOperation : public ConversionOperation { + public: + ConvertFloatToVectorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Float To Color Operation + * + * Takes a float result and outputs a color result. All three color channels of the output are + * filled with the input float and the alpha channel is set to 1. */ +class ConvertFloatToColorOperation : public ConversionOperation { + public: + ConvertFloatToColorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Color To Float Operation + * + * Takes a color result and outputs a float result. The output is the average of the three color + * channels, the alpha channel is ignored. */ +class ConvertColorToFloatOperation : public ConversionOperation { + public: + ConvertColorToFloatOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Color To Vector Operation + * + * Takes a color result and outputs a vector result. The output is a copy of the three color + * channels to the three vector components. */ +class ConvertColorToVectorOperation : public ConversionOperation { + public: + ConvertColorToVectorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Vector To Float Operation + * + * Takes a vector result and outputs a float result. The output is the average of the three + * components. */ +class ConvertVectorToFloatOperation : public ConversionOperation { + public: + ConvertVectorToFloatOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Vector To Color Operation + * + * Takes a vector result and outputs a color result. The output is a copy of the three vector + * components to the three color channels with the alpha channel set to 1. */ +class ConvertVectorToColorOperation : public ConversionOperation { + public: + ConvertVectorToColorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_domain.hh b/source/blender/compositor/realtime_compositor/COM_domain.hh new file mode 100644 index 00000000000..a4f9eb68db4 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_domain.hh @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +namespace blender::realtime_compositor { + +/* Possible interpolations to use when realizing an input result of some domain on another domain. + * See the RealizationOptions struct for more information. */ +enum class Interpolation : uint8_t { + Nearest, + Bilinear, + Bicubic, +}; + +/* ------------------------------------------------------------------------------------------------ + * Realization Options + * + * The options that describe how an input result prefer to be realized on some other domain. This + * is used by the Realize On Domain Operation to identify the appropriate method of realization. + * See the Domain class for more information. */ +struct RealizationOptions { + /* The interpolation method that should be used when performing realization. Since realizing a + * result involves projecting it on a different domain, which in turn, involves sampling the + * result at arbitrary locations, the interpolation identifies the method used for computing the + * value at those arbitrary locations. */ + Interpolation interpolation = Interpolation::Nearest; + /* If true, the result will be repeated infinitely along the horizontal axis when realizing the + * result. If false, regions outside of bounds of the result along the horizontal axis will be + * filled with zeros. */ + bool repeat_x = false; + /* If true, the result will be repeated infinitely along the vertical axis when realizing the + * result. If false, regions outside of bounds of the result along the vertical axis will be + * filled with zeros. */ + bool repeat_y = false; +}; + +/* ------------------------------------------------------------------------------------------------ + * Domain + * + * The compositor is designed in such a way as to allow compositing in an infinite virtual + * compositing space. Consequently, any result of an operation is not only represented by its image + * output, but also by its transformation in that virtual space. The transformation of the result + * together with the dimension of its image is stored and represented by a Domain. In the figure + * below, two results of different domains are illustrated on the virtual compositing space. One of + * the results is centered in space with an image dimension of 800px × 600px, and the other result + * is scaled down and translated such that it lies in the upper right quadrant of the space with an + * image dimension of 800px × 400px. The position of the domain is in pixel space, and the domain + * is considered centered if it has an identity transformation. Note that both results have the + * same resolution, but occupy different areas of the virtual compositing space. + * + * y + * ^ + * 800px x 600px | + * .---------------------|---------------------. + * | | 800px x 600px | + * | | .-------------. | + * | | | | | + * | | '-------------' | + * ------|---------------------|---------------------|------> x + * | | | + * | | | + * | | | + * | | | + * '---------------------|---------------------' + * | + * + * By default, results have domains of identity transformations, that is, they are centered in + * space, but a transformation operation like the rotate, translate, or transform operations will + * adjust the transformation to make the result reside somewhere different in space. The domain of + * a single value result is irrelevant and always set to an identity domain. + * + * An operation is typically only concerned about a subset of the virtual compositing space, this + * subset is represented by a domain which is called the Operation Domain. It follows that before + * the operation itself is executed, inputs will typically be realized on the operation domain to + * be in the same domain and have the same dimension as that of the operation domain. This process + * is called Domain Realization and is implemented using an operation called the Realize On Domain + * Operation. Realization involves projecting the result onto the target domain, copying the area + * of the result that intersects the target domain, and filling the rest with zeros or repetitions + * of the result depending on the realization options that can be set by the user. Consequently, + * operations can generally expect their inputs to have the same dimension and can operate on them + * directly and transparently. For instance, if an operation takes both results illustrated in + * the figure above, and the operation has an operation domain that matches the bigger domain, the + * result with the bigger domain will not need to be realized because it already has a domain that + * matches that of the operation domain, but the result with the smaller domain will have to be + * realized into a new result that has the same domain as the domain of the bigger result. Assuming + * no repetition, the output of the realization will be an all zeros image with dimension 800px × + * 600px with a small scaled version of the smaller result copied into the upper right quadrant of + * the image. The following figure illustrates the realization process on a different set of + * results + * + * Realized Result + * +-------------+ +-------------+ + * | Operation | | | + * | Domain | | Zeros | + * | | ----> | | + * +-----|-----+ | |-----+ | + * | | C | | | C | | + * | +-----|-------+ +-----'-------+ + * | Domain Of | + * | Input | + * +-----------+ + * + * An operation can operate in an arbitrary operation domain, but in most cases, the operation + * domain is inferred from the inputs of the operation. In particular, one of the inputs is said to + * be the Domain Input of the operation and the operation domain is inferred from its domain. It + * follows that this particular input will not need realization, because it already has the correct + * domain. The domain input selection mechanism is as follows. Each of the inputs are assigned a + * value by the developer called the Domain Priority, the domain input is then chosen as the + * non-single value input with the highest domain priority, zero being the highest priority. See + * Operation::compute_domain for more information. + * + * The aforementioned logic for operation domain computation is only a default that works for most + * cases, but an operation can override the compute_domain method to implement a different logic. + * For instance, output nodes have an operation domain the same size as the viewport and with an + * identity transformation, their operation domain doesn't depend on the inputs at all. + * + * For instance, a filter operation has two inputs, a factor and a color, the latter of which is + * assigned a domain priority of 0 and the former is assigned a domain priority of 1. If the color + * input is not a single value input, then the color input is considered to be the domain input of + * the operation and the operation domain is computed to be the same domain as the color input, + * because it has the highest priority. It follows that if the factor input has a different domain + * than the computed domain of the operation, it will be projected and realized on it to have the + * same domain as described above. On the other hand, if the color input is a single value input, + * then the factor input is considered to be the domain input and the operation domain will be the + * same as the domain of the factor input, because it has the second highest domain priority. + * Finally, if both inputs are single value inputs, the operation domain will be an identity domain + * and is irrelevant, because the output will be a domain-less single value. */ +class Domain { + public: + /* The size of the domain in pixels. */ + int2 size; + /* The 2D transformation of the domain defining its translation in pixels, rotation, and scale in + * the virtual compositing space. */ + float3x3 transformation; + /* The options that describe how this domain prefer to be realized on some other domain. See the + * RealizationOptions struct for more information. */ + RealizationOptions realization_options; + + public: + /* A size only constructor that sets the transformation to identity. */ + Domain(int2 size); + + Domain(int2 size, float3x3 transformation); + + /* Transform the domain by the given transformation. This effectively pre-multiply the given + * transformation by the current transformation of the domain. */ + void transform(const float3x3 &transformation); + + /* Returns a domain of size 1x1 and an identity transformation. */ + static Domain identity(); +}; + +/* Compare the size and transformation of the domain. The realization_options are not compared + * because they only describe the method of realization on another domain, which is not technically + * a property of the domain itself. */ +bool operator==(const Domain &a, const Domain &b); + +/* Inverse of the above equality operator. */ +bool operator!=(const Domain &a, const Domain &b); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_evaluator.hh b/source/blender/compositor/realtime_compositor/COM_evaluator.hh new file mode 100644 index 00000000000..fd6feb0948b --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_evaluator.hh @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> + +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_context.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_shader_operation.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Evaluator + * + * The evaluator is the main class of the compositor and the entry point of its execution. The + * evaluator compiles the compositor node tree and evaluates it to compute its output. It is + * constructed from a compositor node tree and a compositor context. Upon calling the evaluate + * method, the evaluator will check if the node tree is already compiled into an operations stream, + * and if it is, it will go over it and evaluate the operations in order. It is then the + * responsibility of the caller to call the reset method when the node tree changes to invalidate + * the operations stream. A reset is also required if the resources used by the node tree change, + * for instances, when the dimensions of an image used by the node tree changes. This is necessary + * because the evaluator compiles the node tree into an operations stream that is specifically + * optimized for the structure of the resources used by the node tree. + * + * Otherwise, if the node tree is not yet compiled, the evaluator will compile it into an + * operations stream, evaluating the operations in the process. It should be noted that operations + * are evaluated as soon as they are compiled, as opposed to compiling the whole operations stream + * and then evaluating it in a separate step. This is important because, as mentioned before, the + * operations stream is optimized specifically for the structure of the resources used by the node + * tree, which is only known after the operations are evaluated. In other words, the evaluator uses + * the evaluated results of previously compiled operations to compile the operations that follow + * them in an optimized manner. + * + * Compilation starts by computing an optimized node execution schedule by calling the + * compute_schedule function, see the discussion in COM_scheduler.hh for more details. For the node + * tree shown below, the execution schedule is denoted by the node numbers. The compiler then goes + * over the execution schedule in order and compiles each node into either a Node Operation or a + * Shader Operation, depending on the node type, see the is_shader_node function. A Shader + * operation is constructed from a group of nodes forming a contiguous subset of the node execution + * schedule. For instance, in the node tree shown below, nodes 3 and 4 are compiled together into a + * shader operation and node 5 is compiled into its own shader operation, both of which are + * contiguous subsets of the node execution schedule. This process is described in details in the + * following section. + * + * Shader Operation 1 Shader Operation 2 + * +-----------------------------------+ +------------------+ + * .------------. | .------------. .------------. | | .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 | + * | |----|--| |--| |---|-----|--| |--|--| | + * | | .-|--| | | | | .--|--| | | | | + * '------------' | | '------------' '------------' | | | '------------' | '------------' + * | +-----------------------------------+ | +------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'----------------------------------------' + * | | + * '------------' + * + * For non shader nodes, the compilation process is straight forward, the compiler instantiates a + * node operation from the node, map its inputs to the results of the outputs they are linked to, + * and evaluates the operations. However, for shader nodes, since a group of nodes can be compiled + * together into a shader operation, the compilation process is a bit involved. The compiler uses + * an instance of the Compile State class to keep track of the compilation process. The compiler + * state stores the so called "shader compile unit", which is the current group of nodes that will + * eventually be compiled together into a shader operation. While going over the schedule, the + * compiler adds the shader nodes to the compile unit until it decides that the compile unit is + * complete and should be compiled. This is typically decided when the current node is not + * compatible with the compile unit and can't be added to it, only then it compiles the compile + * unit into a shader operation and resets it to ready it to track the next potential group of + * nodes that will form a shader operation. This decision is made based on various criteria in the + * should_compile_shader_compile_unit function. See the discussion in COM_compile_state.hh for more + * details of those criteria, but perhaps the most evident of which is whether the node is actually + * a shader node, if it isn't, then it evidently can't be added to the compile unit and the compile + * unit is should be compiled. + * + * For the node tree above, the compilation process is as follows. The compiler goes over the node + * execution schedule in order considering each node. Nodes 1 and 2 are not shader node so they are + * compiled into node operations and added to the operations stream. The current compile unit is + * empty, so it is not compiled. Node 3 is a shader node, and since the compile unit is currently + * empty, it is unconditionally added to it. Node 4 is a shader node, it was decided---for the sake + * of the demonstration---that it is compatible with the compile unit and can be added to it. Node + * 5 is a shader node, but it was decided---for the sake of the demonstration---that it is not + * compatible with the compile unit, so the compile unit is considered complete and is compiled + * first, adding the first shader operation to the operations stream and resetting the compile + * unit. Node 5 is then added to the now empty compile unit similar to node 3. Node 6 is not a + * shader node, so the compile unit is considered complete and is compiled first, adding the first + * shader operation to the operations stream and resetting the compile unit. Finally, node 6 is + * compiled into a node operation similar to nodes 1 and 2 and added to the operations stream. */ +class Evaluator { + private: + /* A reference to the compositor context. */ + Context &context_; + /* A reference to the compositor node tree. */ + bNodeTree &node_tree_; + /* The derived and reference node trees representing the compositor node tree. Those are + * initialized when the node tree is compiled and freed when the evaluator resets. */ + NodeTreeRefMap node_tree_reference_map_; + std::unique_ptr<DerivedNodeTree> derived_node_tree_; + /* The compiled operations stream. This contains ordered pointers to the operations that were + * compiled. This is initialized when the node tree is compiled and freed when the evaluator + * resets. The is_compiled_ member indicates whether the operation stream can be used or needs to + * be compiled first. Note that the operations stream can be empty even when compiled, this can + * happen when the node tree is empty or invalid for instance. */ + Vector<std::unique_ptr<Operation>> operations_stream_; + /* True if the node tree is already compiled into an operations stream that can be evaluated + * directly. False if the node tree is not compiled yet and needs to be compiled. */ + bool is_compiled_ = false; + + public: + /* Construct an evaluator from a compositor node tree and a context. */ + Evaluator(Context &context, bNodeTree &node_tree); + + /* Evaluate the compositor node tree. If the node tree is already compiled into an operations + * stream, that stream will be evaluated directly. Otherwise, the node tree will be compiled and + * evaluated. */ + void evaluate(); + + /* Invalidate the operations stream that was compiled for the node tree. This should be called + * when the node tree changes or the structure of any of the resources used by it changes. By + * structure, we mean things like the dimensions of the used images, while changes to their + * contents do not necessitate a reset. */ + void reset(); + + private: + /* Check if the compositor node tree is valid by checking if it has: + * - Cyclic links. + * - Undefined nodes or sockets. + * - Unsupported nodes. + * If the node tree is valid, true is returned. Otherwise, false is returned, and an appropriate + * error message is set by calling the context's set_info_message method. */ + bool validate_node_tree(); + + /* Compile the node tree into an operations stream and evaluate it. */ + void compile_and_evaluate(); + + /* Compile the given node into a node operation, map each input to the result of the output + * linked to it, update the compile state, add the newly created operation to the operations + * stream, and evaluate the operation. */ + void compile_and_evaluate_node(DNode node, CompileState &compile_state); + + /* Map each input of the node operation to the result of the output linked to it. Unlinked inputs + * are mapped to the result of a newly created Input Single Value Operation, which is added to + * the operations stream and evaluated. Since this method might add operations to the operations + * stream, the actual node operation should only be added to the stream once this method is + * called. */ + void map_node_operation_inputs_to_their_results(DNode node, + NodeOperation *operation, + CompileState &compile_state); + + /* Compile the shader compile unit into a shader operation, map each input of the operation to + * the result of the output linked to it, update the compile state, add the newly created + * operation to the operations stream, evaluate the operation, and finally reset the shader + * compile unit. */ + void compile_and_evaluate_shader_compile_unit(CompileState &compile_state); + + /* Map each input of the shader operation to the result of the output linked to it. */ + void map_shader_operation_inputs_to_their_results(ShaderOperation *operation, + CompileState &compile_state); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh new file mode 100644 index 00000000000..215a92ab3b4 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Input Descriptor + * + * A class that describes an input of an operation. */ +class InputDescriptor { + public: + /* The type of input. This may be different that the type of result that the operation will + * receive for the input, in which case, an implicit conversion operation will be added as an + * input processor to convert it to the required type. */ + ResultType type; + /* If true, then the input does not need to be realized on the domain of the operation before its + * execution. See the discussion in COM_domain.hh for more information. */ + bool skip_realization = false; + /* The priority of the input for determining the operation domain. The non-single value input + * with the highest priority will be used to infer the operation domain, the highest priority + * being zero. See the discussion in COM_domain.hh for more information. */ + int domain_priority = 0; + /* If true, the input expects a single value, and if a non-single value is provided, a default + * single value will be used instead, see the get_<type>_value_default methods in the Result + * class. It follows that this also implies skip_realization, because we don't need to realize a + * result that will be discarded anyways. If false, the input can work with both single and + * non-single values. */ + bool expects_single_value = false; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh new file mode 100644 index 00000000000..bbcd336ee11 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Input Single Value Operation + * + * An input single value operation is an operation that outputs a single value result whose value + * is the value of an unlinked input socket. This is typically used to initialize the values of + * unlinked node input sockets. */ +class InputSingleValueOperation : public Operation { + private: + /* The identifier of the output. */ + static const StringRef output_identifier_; + /* The input socket whose value will be computed as the operation's result. */ + DInputSocket input_socket_; + + public: + InputSingleValueOperation(Context &context, DInputSocket input_socket); + + /* Allocate a single value result and set its value to the default value of the input socket. */ + void execute() override; + + /* Get a reference to the output result of the operation, this essentially calls the super + * get_result with the output identifier of the operation. */ + Result &get_result(); + + private: + /* Populate the result of the operation, this essentially calls the super populate_result method + * with the output identifier of the operation. */ + void populate_result(Result result); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_node_operation.hh b/source/blender/compositor/realtime_compositor/COM_node_operation.hh new file mode 100644 index 00000000000..94bc4dfd39d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_node_operation.hh @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_scheduler.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Node Operation + * + * A node operation is a subclass of operation that nodes should implement and instantiate in the + * get_compositor_operation function of bNodeType, passing the inputs given to that function to the + * constructor. This class essentially just implements a default constructor that populates output + * results for all outputs of the node as well as input descriptors for all inputs of the nodes + * based on their socket declaration. The class also provides some utility methods for easier + * implementation of nodes. */ +class NodeOperation : public Operation { + private: + /* The node that this operation represents. */ + DNode node_; + + public: + /* Populate the output results based on the node outputs and populate the input descriptors based + * on the node inputs. */ + NodeOperation(Context &context, DNode node); + + /* Compute and set the initial reference counts of all the results of the operation. The + * reference counts of the results are the number of operations that use those results, which is + * computed as the number of inputs whose node is part of the schedule and is linked to the + * output corresponding to each result. The node execution schedule is given as an input. */ + void compute_results_reference_counts(const Schedule &schedule); + + protected: + /* Returns a reference to the derived node that this operation represents. */ + const DNode &node() const; + + /* Returns a reference to the node that this operation represents. */ + const bNode &bnode() const; + + /* Returns true if the output identified by the given identifier is needed and should be + * computed, otherwise returns false. */ + bool should_compute_output(StringRef identifier); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_operation.hh b/source/blender/compositor/realtime_compositor/COM_operation.hh new file mode 100644 index 00000000000..38518c00c04 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_operation.hh @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> +#include <string> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +class SimpleOperation; + +/* A type representing a vector of simple operations that store the input processors for a + * particular input. */ +using ProcessorsVector = Vector<std::unique_ptr<SimpleOperation>>; + +/* ------------------------------------------------------------------------------------------------ + * Operation + * + * The operation is the basic unit of the compositor. The evaluator compiles the compositor node + * tree into an ordered stream of operations which are then executed in order during evaluation. + * The operation class can be sub-classed to implement a new operation. Operations have a number of + * inputs and outputs that are declared during construction and are identified by string + * identifiers. Inputs are declared by calling declare_input_descriptor providing an appropriate + * descriptor. Those inputs are mapped to the results computed by other operations whose outputs + * are linked to the inputs. Such mappings are established by the compiler during compilation by + * calling the map_input_to_result method. Outputs are populated by calling the populate_result + * method, providing a result of an appropriate type. Upon execution, the operation allocates a + * result for each of its outputs and computes their value based on its inputs and options. + * + * Each input may have one or more input processors, which are simple operations that process the + * inputs before the operation is executed, see the discussion in COM_simple_operation.hh for more + * information. And thus the effective input of the operation is the result of the last input + * processor if one exists. Input processors are added and evaluated by calling the + * add_and_evaluate_input_processors method, which provides a default implementation that does + * things like implicit conversion, domain realization, and more. This default implementation can, + * however, be overridden, extended, or removed. Once the input processors are added and evaluated + * for the first time, they are stored in the operation and future evaluations can evaluate them + * directly without having to add them again. + * + * The operation is evaluated by calling the evaluate method, which first adds the input processors + * if they weren't added already and evaluates them, then it resets the results of the operation, + * then it calls the execute method of the operation, and finally it releases the results mapped to + * the inputs to declare that they are no longer needed. */ +class Operation { + private: + /* A reference to the compositor context. This member references the same object in all + * operations but is included in the class for convenience. */ + Context &context_; + /* A mapping between each output of the operation identified by its identifier and the result for + * that output. A result for each output of the operation should be constructed and added to the + * map during operation construction by calling the populate_result method. The results should be + * allocated and their contents should be computed in the execute method. */ + Map<std::string, Result> results_; + /* A mapping between each input of the operation identified by its identifier and its input + * descriptor. Those descriptors should be declared during operation construction by calling the + * declare_input_descriptor method. */ + Map<std::string, InputDescriptor> input_descriptors_; + /* A mapping between each input of the operation identified by its identifier and a pointer to + * the computed result providing its data. The mapped result is either one that was computed by + * another operation or one that was internally computed in the operation by the last input + * processor for that input. It is the responsibility of the evaluator to map the inputs to their + * linked results before evaluating the operation by calling the map_input_to_result method. */ + Map<StringRef, Result *> results_mapped_to_inputs_; + /* A mapping between each input of the operation identified by its identifier and an ordered list + * of simple operations to process that input. This is initialized the first time the input + * processors are evaluated by calling the add_and_evaluate_input_processors method. Further + * evaluations will evaluate the processors directly without the need to add them again. The + * input_processors_added_ member indicates whether the processors were already added and can be + * evaluated directly or need to be added and evaluated. */ + Map<StringRef, ProcessorsVector> input_processors_; + /* True if the input processors were already added and can be evaluated directly. False if the + * input processors are not yet added and needs to be added. */ + bool input_processors_added_ = false; + + public: + Operation(Context &context); + + virtual ~Operation(); + + /* Evaluate the operation by: + * 1. Evaluating the input processors. + * 2. Resetting the results of the operation. + * 3. Calling the execute method of the operation. + * 4. Releasing the results mapped to the inputs. */ + void evaluate(); + + /* Get a reference to the output result identified by the given identifier. */ + Result &get_result(StringRef identifier); + + /* Map the input identified by the given identifier to the result providing its data. See + * results_mapped_to_inputs_ for more details. This should be called by the evaluator to + * establish links between different operations. */ + void map_input_to_result(StringRef identifier, Result *result); + + protected: + /* Compute the operation domain of this operation. By default, this implements a default logic + * that infers the operation domain from the inputs, which may be overridden for a different + * logic. See the discussion in COM_domain.hh for the inference logic and more information. */ + virtual Domain compute_domain(); + + /* Add and evaluate any needed input processors, which essentially just involves calling the + * add_and_evaluate_input_processor method with the needed processors. This is called before + * executing the operation to prepare its inputs. The class defines a default implementation + * which adds typically needed processors, but derived classes can override the method to have + * a different implementation, extend the implementation, or remove it entirely. */ + virtual void add_and_evaluate_input_processors(); + + /* Given the identifier of an input of the operation and a processor operation: + * - Add the given processor to the list of input processors for the input. + * - Map the input of the processor to be the result of the last input processor or the result + * mapped to the input if no previous processors exists. + * - Switch the result mapped to the input to be the output result of the processor. + * - Evaluate the processor. */ + void add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor); + + /* This method should allocate the operation results, execute the operation, and compute the + * output results. */ + virtual void execute() = 0; + + /* Get a reference to the result connected to the input identified by the given identifier. */ + Result &get_input(StringRef identifier) const; + + /* Switch the result mapped to the input identified by the given identifier with the given + * result. */ + void switch_result_mapped_to_input(StringRef identifier, Result *result); + + /* Add the given result to the results_ map identified by the given output identifier. This + * should be called during operation construction for all outputs. The provided result shouldn't + * be allocated or initialized, this will happen later during execution. */ + void populate_result(StringRef identifier, Result result); + + /* Declare the descriptor of the input identified by the given identifier to be the given + * descriptor. Adds the given descriptor to the input_descriptors_ map identified by the given + * input identifier. This should be called during operation constructor for all inputs. */ + void declare_input_descriptor(StringRef identifier, InputDescriptor descriptor); + + /* Get a reference to the descriptor of the input identified by the given identified. */ + InputDescriptor &get_input_descriptor(StringRef identifier); + + /* Returns a reference to the compositor context. */ + Context &context(); + + /* Returns a reference to the texture pool of the compositor context. */ + TexturePool &texture_pool() const; + + /* Returns a reference to the shader manager of the compositor context. */ + StaticShaderManager &shader_manager() const; + + private: + /* Evaluate the input processors. If the input processors were already added they will be + * evaluated directly. Otherwise, the input processors will be added and evaluated. */ + void evaluate_input_processors(); + + /* Resets the results of the operation. See the reset method in the Result class for more + * information. */ + void reset_results(); + + /* Release the results that are mapped to the inputs of the operation. This is called after the + * evaluation of the operation to declare that the results are no longer needed by this + * operation. */ + void release_inputs(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh new file mode 100644 index 00000000000..5a842e16008 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Realize On Domain Operation + * + * A simple operation that projects the input on a certain target domain, copies the area of the + * input that intersects the target domain, and fill the rest with zeros or repetitions of the + * input depending on the realization options of the target domain. See the discussion in + * COM_domain.hh for more information. */ +class RealizeOnDomainOperation : public SimpleOperation { + private: + /* The target domain to realize the input on. */ + Domain domain_; + + public: + RealizeOnDomainOperation(Context &context, Domain domain, ResultType type); + + void execute() override; + + /* Determine if a realize on domain operation is needed for the input with the given result and + * descriptor in an operation with the given operation domain. If it is not needed, return a null + * pointer. If it is needed, return an instance of the operation. */ + static SimpleOperation *construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor, + const Domain &operation_domain); + + protected: + /* The operation domain is just the target domain. */ + Domain compute_domain() override; + + private: + /* Get the realization shader of the appropriate type. */ + GPUShader *get_realization_shader(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh new file mode 100644 index 00000000000..2f5a82c79b7 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "COM_context.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Reduce To Single Value Operation + * + * A simple operation that reduces its input result into a single value output result. The input is + * assumed to be a texture result of size 1x1, that is, a texture composed of a single pixel, the + * value of which shall serve as the single value of the output result. */ +class ReduceToSingleValueOperation : public SimpleOperation { + public: + ReduceToSingleValueOperation(Context &context, ResultType type); + + /* Download the input pixel from the GPU texture and set its value to the value of the allocated + * single value output result. */ + void execute() override; + + /* Determine if a reduce to single value operation is needed for the input with the + * given result. If it is not needed, return a null pointer. If it is needed, return an instance + * of the operation. */ + static SimpleOperation *construct_if_needed(Context &context, const Result &input_result); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_result.hh b/source/blender/compositor/realtime_compositor/COM_result.hh new file mode 100644 index 00000000000..a16d68bb92d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_result.hh @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_domain.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* Possible data types that operations can operate on. They either represent the base type of the + * result texture or a single value result. */ +enum class ResultType : uint8_t { + Float, + Vector, + Color, +}; + +/* ------------------------------------------------------------------------------------------------ + * Result + * + * A result represents the computed value of an output of an operation. A result can either + * represent an image or a single value. A result is typed, and can be of type color, vector, or + * float. Single value results are stored in 1x1 textures to make them easily accessible in + * shaders. But the same value is also stored in the value union member of the result for any + * host-side processing. The texture of the result is allocated from the texture pool referenced by + * the result. + * + * Results are reference counted and their textures are released once their reference count reaches + * zero. After constructing a result, the set_initial_reference_count method is called to declare + * the number of operations that needs this result. Once each operation that needs the result no + * longer needs it, the release method is called and the reference count is decremented, until it + * reaches zero, where the result's texture is then released. Since results are eventually + * decremented to zero by the end of every evaluation, the reference count is restored before every + * evaluation to its initial reference count by calling the reset method, which is why a separate + * member initial_reference_count_ is stored to keep track of the initial value. + * + * A result not only represents an image, but also the area it occupies in the virtual compositing + * space. This area is called the Domain of the result, see the discussion in COM_domain.hh for + * more information. + * + * A result can be a proxy result that merely wraps another master result, in which case, it shares + * its values and delegates all reference counting to it. While a proxy result shares the value of + * the master result, it can have a different domain. Consequently, transformation operations are + * implemented using proxy results, where their results are proxy results of their inputs but with + * their domains transformed based on their options. Moreover, proxy results can also be used as + * the results of identity operations, that is, operations that do nothing to their inputs in + * certain configurations. In which case, the proxy result is left as is with no extra + * transformation on its domain whatsoever. Proxy results can be created by calling the + * pass_through method, see that method for more details. */ +class Result { + private: + /* The base type of the texture or the type of the single value. */ + ResultType type_; + /* If true, the result is a single value, otherwise, the result is a texture. */ + bool is_single_value_; + /* A GPU texture storing the result data. This will be a 1x1 texture if the result is a single + * value, the value of which will be identical to that of the value member. See class description + * for more information. */ + GPUTexture *texture_ = nullptr; + /* The texture pool used to allocate the texture of the result, this should be initialized during + * construction. */ + TexturePool *texture_pool_ = nullptr; + /* The number of operations that currently needs this result. At the time when the result is + * computed, this member will have a value that matches initial_reference_count_. Once each + * operation that needs the result no longer needs it, the release method is called and the + * reference count is decremented, until it reaches zero, where the result's texture is then + * released. If this result have a master result, then this reference count is irrelevant and + * shadowed by the reference count of the master result. */ + int reference_count_; + /* The number of operations that reference and use this result at the time when it was initially + * computed. Since reference_count_ is decremented and always becomes zero at the end of the + * evaluation, this member is used to reset the reference count of the results for later + * evaluations by calling the reset method. This member is also used to determine if this result + * should be computed by calling the should_compute method. */ + int initial_reference_count_; + /* If the result is a single value, this member stores the value of the result, the value of + * which will be identical to that stored in the texture member. The active union member depends + * on the type of the result. This member is uninitialized and should not be used if the result + * is a texture. */ + union { + float float_value_; + float3 vector_value_; + float4 color_value_; + }; + /* The domain of the result. This only matters if the result was a texture. See the discussion in + * COM_domain.hh for more information. */ + Domain domain_ = Domain::identity(); + /* If not nullptr, then this result wraps and shares the value of another master result. In this + * case, calls to texture-related methods like increment_reference_count and release should + * operate on the master result as opposed to this result. This member is typically set upon + * calling the pass_through method, which sets this result to be the master of a target result. + * See that method for more information. */ + Result *master_ = nullptr; + + public: + /* Construct a result of the given type with the given texture pool that will be used to allocate + * and release the result's texture. */ + Result(ResultType type, TexturePool &texture_pool); + + /* Declare the result to be a texture result, allocate a texture of an appropriate type with + * the size of the given domain from the result's texture pool, and set the domain of the result + * to the given domain. */ + void allocate_texture(Domain domain); + + /* Declare the result to be a single value result, allocate a texture of an appropriate + * type with size 1x1 from the result's texture pool, and set the domain to be an identity + * domain. See class description for more information. */ + void allocate_single_value(); + + /* Allocate a single value result and set its value to zero. This is called for results whose + * value can't be computed and are considered invalid. */ + void allocate_invalid(); + + /* Bind the texture of the result to the texture image unit with the given name in the currently + * bound given shader. This also inserts a memory barrier for texture fetches to ensure any prior + * writes to the texture are reflected before reading from it. */ + void bind_as_texture(GPUShader *shader, const char *texture_name) const; + + /* Bind the texture of the result to the image unit with the given name in the currently bound + * given shader. */ + void bind_as_image(GPUShader *shader, const char *image_name) const; + + /* Unbind the texture which was previously bound using bind_as_texture. */ + void unbind_as_texture() const; + + /* Unbind the texture which was previously bound using bind_as_image. */ + void unbind_as_image() const; + + /* Pass this result through to a target result, in which case, the target result becomes a proxy + * result with this result as its master result. This is done by making the target result a copy + * of this result, essentially having identical values between the two and consequently sharing + * the underlying texture. An exception is the initial reference count, whose value is retained + * and not copied, because it is a property of the original result and is needed for correctly + * resetting the result before the next evaluation. Additionally, this result is set to be the + * master of the target result, by setting the master member of the target. Finally, the + * reference count of the result is incremented by the reference count of the target result. See + * the discussion above for more information. */ + void pass_through(Result &target); + + /* Transform the result by the given transformation. This effectively pre-multiply the given + * transformation by the current transformation of the domain of the result. */ + void transform(const float3x3 &transformation); + + /* Get a reference to the realization options of this result. See the RealizationOptions struct + * for more information. */ + RealizationOptions &get_realization_options(); + + /* If the result is a single value result of type float, return its float value. Otherwise, an + * uninitialized value is returned. */ + float get_float_value() const; + + /* If the result is a single value result of type vector, return its vector value. Otherwise, an + * uninitialized value is returned. */ + float3 get_vector_value() const; + + /* If the result is a single value result of type color, return its color value. Otherwise, an + * uninitialized value is returned. */ + float4 get_color_value() const; + + /* Same as get_float_value but returns a default value if the result is not a single value. */ + float get_float_value_default(float default_value) const; + + /* Same as get_vector_value but returns a default value if the result is not a single value. */ + float3 get_vector_value_default(const float3 &default_value) const; + + /* Same as get_color_value but returns a default value if the result is not a single value. */ + float4 get_color_value_default(const float4 &default_value) const; + + /* If the result is a single value result of type float, set its float value and upload it to the + * texture. Otherwise, an undefined behavior is invoked. */ + void set_float_value(float value); + + /* If the result is a single value result of type vector, set its vector value and upload it to + * the texture. Otherwise, an undefined behavior is invoked. */ + void set_vector_value(const float3 &value); + + /* If the result is a single value result of type color, set its color value and upload it to the + * texture. Otherwise, an undefined behavior is invoked. */ + void set_color_value(const float4 &value); + + /* Set the value of initial_reference_count_, see that member for more details. This should be + * called after constructing the result to declare the number of operations that needs it. */ + void set_initial_reference_count(int count); + + /* Reset the result to prepare it for a new evaluation. This should be called before evaluating + * the operation that computes this result. First, set the value of reference_count_ to the value + * of initial_reference_count_ since reference_count_ may have already been decremented to zero + * in a previous evaluation. Second, set master_ to nullptr because the result may have been + * turned into a proxy result in a previous evaluation. Other fields don't need to be reset + * because they are runtime and overwritten during evaluation. */ + void reset(); + + /* Increment the reference count of the result by the given count. If this result have a master + * result, the reference count of the master result is incremented instead. */ + void increment_reference_count(int count = 1); + + /* Decrement the reference count of the result and release the its texture back into the texture + * pool if the reference count reaches zero. This should be called when an operation that used + * this result no longer needs it. If this result have a master result, the master result is + * released instead. */ + void release(); + + /* Returns true if this result should be computed and false otherwise. The result should be + * computed if its reference count is not zero, that is, its result is used by at least one + * operation. */ + bool should_compute(); + + /* Returns the type of the result. */ + ResultType type() const; + + /* Returns true if the result is a texture and false of it is a single value. */ + bool is_texture() const; + + /* Returns true if the result is a single value and false of it is a texture. */ + bool is_single_value() const; + + /* Returns the allocated GPU texture of the result. */ + GPUTexture *texture() const; + + /* Returns the reference count of the result. If this result have a master result, then the + * reference count of the master result is returned instead. */ + int reference_count() const; + + /* Returns a reference to the domain of the result. See the Domain class. */ + const Domain &domain() const; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_scheduler.hh b/source/blender/compositor/realtime_compositor/COM_scheduler.hh new file mode 100644 index 00000000000..4f778b32145 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_scheduler.hh @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_vector_set.hh" + +#include "NOD_derived_node_tree.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* A type representing the ordered set of nodes defining the schedule of node execution. */ +using Schedule = VectorSet<DNode>; + +/* Computes the execution schedule of the node tree. This is essentially a post-order depth first + * traversal of the node tree from the output node to the leaf input nodes, with informed order of + * traversal of dependencies based on a heuristic estimation of the number of needed buffers. */ +Schedule compute_schedule(DerivedNodeTree &tree); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_shader_node.hh b/source/blender/compositor/realtime_compositor/COM_shader_node.hh new file mode 100644 index 00000000000..453226ec452 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_shader_node.hh @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "GPU_material.h" + +#include "NOD_derived_node_tree.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Shader Node + * + * A shader node encapsulates a compositor node tree that is capable of being used together with + * other shader nodes to construct a Shader Operation using the GPU material compiler. A GPU node + * stack for each of the node inputs and outputs is stored and populated during construction in + * order to represent the node as a GPU node inside the GPU material graph, see GPU_material.h for + * more information. Derived classes should implement the compile method to add the node and link + * it to the GPU material given to the method. The compiler is expected to initialize the input + * links of the node before invoking the compile method. See the discussion in + * COM_shader_operation.hh for more information. */ +class ShaderNode { + private: + /* The node that this operation represents. */ + DNode node_; + /* The GPU node stacks of the inputs of the node. Those are populated during construction in the + * populate_inputs method. The links of the inputs are initialized by the GPU material compiler + * prior to calling the compile method. There is an extra stack at the end to mark the end of the + * array, as this is what the GPU module functions expect. */ + Vector<GPUNodeStack> inputs_; + /* The GPU node stacks of the outputs of the node. Those are populated during construction in the + * populate_outputs method. There is an extra stack at the end to mark the end of the array, as + * this is what the GPU module functions expect. */ + Vector<GPUNodeStack> outputs_; + + public: + /* Construct the node by populating both its inputs and outputs. */ + ShaderNode(DNode node); + + virtual ~ShaderNode() = default; + + /* Compile the node by adding the appropriate GPU material graph nodes and linking the + * appropriate resources. */ + virtual void compile(GPUMaterial *material) = 0; + + /* Returns a contiguous array containing the GPU node stacks of each input. */ + GPUNodeStack *get_inputs_array(); + + /* Returns a contiguous array containing the GPU node stacks of each output. */ + GPUNodeStack *get_outputs_array(); + + /* Returns the GPU node stack of the input with the given identifier. */ + GPUNodeStack &get_input(StringRef identifier); + + /* Returns the GPU node stack of the output with the given identifier. */ + GPUNodeStack &get_output(StringRef identifier); + + /* Returns the GPU node link of the input with the given identifier, if the input is not linked, + * a uniform link carrying the value of the input will be created a returned. It is expected that + * the caller will use the returned link in a GPU material, otherwise, the link may not be + * properly freed. */ + GPUNodeLink *get_input_link(StringRef identifier); + + protected: + /* Returns a reference to the derived node that this operation represents. */ + const DNode &node() const; + + /* Returns a reference to the node this operations represents. */ + bNode &bnode() const; + + private: + /* Populate the inputs of the node. The input link is set to nullptr and is expected to be + * initialized by the GPU material compiler before calling the compile method. */ + void populate_inputs(); + /* Populate the outputs of the node. The output link is set to nullptr and is expected to be + * initialized by the compile method. */ + void populate_outputs(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_shader_operation.hh b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh new file mode 100644 index 00000000000..a33dcbf25be --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector_set.hh" + +#include "GPU_material.h" +#include "GPU_shader.h" + +#include "gpu_shader_create_info.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_scheduler.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* A type representing a contiguous subset of the node execution schedule that will be compiled + * into a Shader Operation. */ +using ShaderCompileUnit = VectorSet<DNode>; + +/* ------------------------------------------------------------------------------------------------ + * Shader Operation + * + * An operation that evaluates a shader compiled from a contiguous subset of the node execution + * schedule using the GPU material compiler, see GPU_material.h for more information. The subset + * of the node execution schedule is called a shader compile unit, see the discussion in + * COM_compile_state.hh for more information. + * + * Consider the following node graph with a node execution schedule denoted by the number on each + * node. The compiler may decide to compile a subset of the execution schedule into a shader + * operation, in this case, the nodes from 3 to 5 were compiled together into a shader operation. + * This subset is called the shader compile unit. See the discussion in COM_evaluator.hh for more + * information on the compilation process. Each of the nodes inside the compile unit implements a + * Shader Node which is instantiated, stored in shader_nodes_, and used during compilation. See the + * discussion in COM_shader_node.hh for more information. Links that are internal to the shader + * operation are established between the input and outputs of the shader nodes, for instance, the + * links between nodes 3 and 4 as well as those between nodes 4 and 5. However, links that cross + * the boundary of the shader operation needs special handling. + * + * Shader Operation + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * Links from nodes that are not part of the shader operation to nodes that are part of the shader + * operation are considered inputs of the operation itself and are declared as such. For instance, + * the link from node 1 to node 3 is declared as an input to the operation, and the same applies + * for the links from node 2 to nodes 3 and 5. Note, however, that only one input is declared for + * each distinct output socket, so both links from node 2 share the same input of the operation. + * An input to the operation is declared for a distinct output socket as follows: + * + * - A texture is added to the shader, which will be bound to the result of the output socket + * during evaluation. + * - A GPU attribute is added to the GPU material for that output socket and is linked to the GPU + * input stack of the inputs linked to the output socket. + * - Code is emitted to initialize the values of the attributes by sampling the textures + * corresponding to each of the inputs. + * - The newly added attribute is mapped to the output socket in output_to_material_attribute_map_ + * to share that same attributes for all inputs linked to the same output socket. + * + * Links from nodes that are part of the shader operation to nodes that are not part of the shader + * operation are considered outputs of the operation itself and are declared as such. For instance, + * the link from node 5 to node 6 is declared as an output to the operation. An output to the + * operation is declared for an output socket as follows: + * + * - An image is added in the shader where the output value will be written. + * - A storer GPU material node that stores the value of the output is added and linked to the GPU + * output stack of the output. The storer will store the value in the image identified by the + * index of the output given to the storer. + * - The storer functions are generated dynamically to map each index with its appropriate image. + * + * The GPU material code generator source is used to construct a compute shader that is then + * dispatched during operation evaluation after binding the inputs, outputs, and any necessary + * resources. */ +class ShaderOperation : public Operation { + private: + /* The compile unit that will be compiled into this shader operation. */ + ShaderCompileUnit compile_unit_; + /* The GPU material backing the operation. This is created and compiled during construction and + * freed during destruction. */ + GPUMaterial *material_; + /* A map that associates each node in the compile unit with an instance of its shader node. */ + Map<DNode, std::unique_ptr<ShaderNode>> shader_nodes_; + /* A map that associates the identifier of each input of the operation with the output socket it + * is linked to. This is needed to help the compiler establish links between operations. */ + Map<std::string, DOutputSocket> inputs_to_linked_outputs_map_; + /* A map that associates the output socket that provides the result of an output of the operation + * with the identifier of that output. This is needed to help the compiler establish links + * between operations. */ + Map<DOutputSocket, std::string> output_sockets_to_output_identifiers_map_; + /* A map that associates the output socket of a node that is not part of the shader operation to + * the attribute that was created for it. This is used to share the same attribute with all + * inputs that are linked to the same output socket. */ + Map<DOutputSocket, GPUNodeLink *> output_to_material_attribute_map_; + + public: + /* Construct and compile a GPU material from the given shader compile unit by calling + * GPU_material_from_callbacks with the appropriate callbacks. */ + ShaderOperation(Context &context, ShaderCompileUnit &compile_unit); + + /* Free the GPU material. */ + ~ShaderOperation(); + + /* Allocate the output results, bind the shader and all its needed resources, then dispatch the + * shader. */ + void execute() override; + + /* Get the identifier of the operation output corresponding to the given output socket. This is + * called by the compiler to identify the operation output that provides the result for an input + * by providing the output socket that the input is linked to. See + * output_sockets_to_output_identifiers_map_ for more information. */ + StringRef get_output_identifier_from_output_socket(DOutputSocket output_socket); + + /* Get a reference to the inputs to linked outputs map of the operation. This is called by the + * compiler to identify the output that each input of the operation is linked to for correct + * input mapping. See inputs_to_linked_outputs_map_ for more information. */ + Map<std::string, DOutputSocket> &get_inputs_to_linked_outputs_map(); + + /* Compute and set the initial reference counts of all the results of the operation. The + * reference counts of the results are the number of operations that use those results, which is + * computed as the number of inputs whose node is part of the schedule and is linked to the + * output corresponding to each of the results of the operation. The node execution schedule is + * given as an input. */ + void compute_results_reference_counts(const Schedule &schedule); + + private: + /* Bind the uniform buffer of the GPU material as well as any color band textures needed by the + * GPU material. The compiled shader of the material is given as an argument and assumed to be + * bound. */ + void bind_material_resources(GPUShader *shader); + + /* Bind the input results of the operation to the appropriate textures in the GPU material. The + * attributes stored in output_to_material_attribute_map_ have names that match the texture + * samplers in the shader as well as the identifiers of the operation inputs that they correspond + * to. The compiled shader of the material is given as an argument and assumed to be bound. */ + void bind_inputs(GPUShader *shader); + + /* Bind the output results of the operation to the appropriate images in the GPU material. The + * name of the images in the shader match the identifier of their corresponding outputs. The + * compiled shader of the material is given as an argument and assumed to be bound. */ + void bind_outputs(GPUShader *shader); + + /* A static callback method of interface ConstructGPUMaterialFn that is passed to + * GPU_material_from_callbacks to construct the GPU material graph. The thunk parameter will be a + * pointer to the instance of ShaderOperation that is being compiled. The method goes over the + * compile unit and does the following for each node: + * + * - Instantiate a ShaderNode from the node and add it to shader_nodes_. + * - Link the inputs of the node if needed. The inputs are either linked to other nodes in the + * GPU material graph or are exposed as inputs to the shader operation itself if they are + * linked to nodes that are not part of the shader operation. + * - Call the compile method of the shader node to actually add and link the GPU material graph + * nodes. + * - If any of the outputs of the node are linked to nodes that are not part of the shader + * operation, they are exposed as outputs to the shader operation itself. */ + static void construct_material(void *thunk, GPUMaterial *material); + + /* Link the inputs of the node if needed. Unlinked inputs are ignored as they will be linked by + * the node compile method. If the input is linked to a node that is not part of the shader + * operation, the input will be exposed as an input to the shader operation and linked to it. + * While if the input is linked to a node that is part of the shader operation, then it is linked + * to that node in the GPU material node graph. */ + void link_node_inputs(DNode node, GPUMaterial *material); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is also part of the shader operation, just link the output + * link of the GPU node stack of the output socket to the input link of the GPU node stack of the + * input socket. This essentially establishes the needed links in the GPU material node graph. */ + void link_node_input_internal(DInputSocket input_socket, DOutputSocket output_socket); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is not part of the shader operation, declare a new + * operation input and link it to the input link of the GPU node stack of the input socket. An + * operation input is only declared if no input was already declared for that same output socket + * before. */ + void link_node_input_external(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is not part of the shader operation, declare a new input to + * the operation that is represented in the GPU material by a newly created GPU attribute. It is + * assumed that no operation input was declared for this same output socket before. In the + * generate_code_for_inputs method, a texture will be added in the shader for each of the + * declared inputs, having the same name as the attribute. Additionally, code will be emitted to + * initialize the attributes by sampling their corresponding textures. */ + void declare_operation_input(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material); + + /* Populate the output results of the shader operation for output sockets of the given node that + * are linked to nodes outside of the shader operation. */ + void populate_results_for_node(DNode node, GPUMaterial *material); + + /* Given the output socket of a node that is part of the shader operation which is linked to an + * input socket of a node that is not part of the shader operation, declare a new output to the + * operation and link it to an output storer passing in the index of the output. In the + * generate_code_for_outputs method, an image will be added in the shader for each of the + * declared outputs. Additionally, code will be emitted to define the storer functions that store + * the value in the appropriate image identified by the given index. */ + void populate_operation_result(DOutputSocket output_socket, GPUMaterial *material); + + /* A static callback method of interface GPUCodegenCallbackFn that is passed to + * GPU_material_from_callbacks to create the shader create info of the GPU material. The thunk + * parameter will be a pointer to the instance of ShaderOperation that is being compiled. + * + * This method first generates the necessary code to load the inputs and store the outputs. Then, + * it creates a compute shader from the generated sources. Finally, it adds the necessary GPU + * resources to the shader. */ + static void generate_code(void *thunk, GPUMaterial *material, GPUCodegenOutput *code_generator); + + /* Add an image in the shader for each of the declared outputs. Additionally, emit code to define + * the storer functions that store the given value in the appropriate image identified by the + * given index. */ + void generate_code_for_outputs(gpu::shader::ShaderCreateInfo &shader_create_info); + + /* Add a texture will in the shader for each of the declared inputs/attributes in the operation, + * having the same name as the attribute. Additionally, emit code to initialize the attributes by + * sampling their corresponding textures. */ + void generate_code_for_inputs(GPUMaterial *material, + gpu::shader::ShaderCreateInfo &shader_create_info); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_simple_operation.hh b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh new file mode 100644 index 00000000000..1655e52ac9a --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "COM_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Simple Operation + * + * A simple operation is an operation that takes exactly one input and computes exactly one output. + * Moreover, the output is guaranteed to only have a single user, that is, its reference count will + * be one. Such operations can be attached to the inputs of operations to pre-process the inputs to + * prepare them before the operation is executed.*/ +class SimpleOperation : public Operation { + private: + /* The identifier of the output. This is constant for all operations. */ + static const StringRef output_identifier_; + /* The identifier of the input. This is constant for all operations. */ + static const StringRef input_identifier_; + + public: + using Operation::Operation; + + /* Get a reference to the output result of the operation, this essentially calls the super + * get_result method with the output identifier of the operation. */ + Result &get_result(); + + /* Map the input of the operation to the given result, this essentially calls the super + * map_input_to_result method with the input identifier of the operation. */ + void map_input_to_result(Result *result); + + protected: + /* Simple operations don't need input processors, so override with an empty implementation. */ + void add_and_evaluate_input_processors() override; + + /* Get a reference to the input result of the operation, this essentially calls the super + * get_result method with the input identifier of the operation. */ + Result &get_input(); + + /* Switch the result mapped to the input with the given result, this essentially calls the super + * switch_result_mapped_to_input method with the input identifier of the operation. */ + void switch_result_mapped_to_input(Result *result); + + /* Populate the result of the operation, this essentially calls the super populate_result method + * with the output identifier of the operation and sets the initial reference count of the result + * to 1, since the result of an operation is guaranteed to have a single user. */ + void populate_result(Result result); + + /* Declare the descriptor of the input of the operation to be the given descriptor, this + * essentially calls the super declare_input_descriptor method with the input identifier of the + * operation. */ + void declare_input_descriptor(InputDescriptor descriptor); + + /* Get a reference to the descriptor of the input, this essentially calls the super + * get_input_descriptor method with the input identifier of the operation. */ + InputDescriptor &get_input_descriptor(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh new file mode 100644 index 00000000000..161a80862a0 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" + +#include "GPU_shader.h" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Static Shader Manager + * + * A static shader manager is a map of shaders identified by their info name that can be acquired + * and reused throughout the evaluation of the compositor and are only freed when the shader + * manager is destroyed. Once a shader is acquired for the first time, it will be cached in the + * manager to be potentially acquired later if needed without the shader creation overhead. */ +class StaticShaderManager { + private: + /* The set of shaders identified by their info name that are currently available in the manager + * to be acquired. */ + Map<StringRef, GPUShader *> shaders_; + + public: + ~StaticShaderManager(); + + /* Check if there is an available shader with the given info name in the manager, if such shader + * exists, return it, otherwise, return a newly created shader and add it to the manager. */ + GPUShader *get(const char *info_name); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_texture_pool.hh b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh new file mode 100644 index 00000000000..cc6641d288f --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_vector.hh" + +#include "GPU_texture.h" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Texture Pool Key + * + * A key used to identify a texture specification in a texture pool. Defines a hash and an equality + * operator for use in a hash map. */ +class TexturePoolKey { + public: + int2 size; + eGPUTextureFormat format; + + /* Construct a key from the given texture size and format. */ + TexturePoolKey(int2 size, eGPUTextureFormat format); + + /* Construct a key from the size and format of the given texture. */ + TexturePoolKey(const GPUTexture *texture); + + uint64_t hash() const; +}; + +bool operator==(const TexturePoolKey &a, const TexturePoolKey &b); + +/* ------------------------------------------------------------------------------------------------ + * Texture Pool + * + * A texture pool allows the allocation and reuse of textures throughout the execution of the + * compositor to avoid memory fragmentation and texture allocation overheads. The texture pool + * delegates the actual texture allocation to an allocate_texture method that should be implemented + * by the caller of the compositor evaluator, allowing a more agnostic and flexible execution that + * can be controlled by the caller. If the compositor is expected to execute frequently, like on + * every redraw, then the allocation method should use a persistent texture pool to allow + * cross-evaluation texture pooling, for instance, by using the DRWTexturePool. But if the + * evaluator is expected to execute infrequently, the allocated textures can just be freed when the + * evaluator is done, that is, when the pool is destructed. */ +class TexturePool { + private: + /* The set of textures in the pool that are available to acquire for each distinct texture + * specification. */ + Map<TexturePoolKey, Vector<GPUTexture *>> textures_; + + public: + /* Check if there is an available texture with the given specification in the pool, if such + * texture exists, return it, otherwise, return a newly allocated texture. Expect the texture to + * be uncleared and possibly contains garbage data. */ + GPUTexture *acquire(int2 size, eGPUTextureFormat format); + + /* Shorthand for acquire with GPU_RGBA16F format. */ + GPUTexture *acquire_color(int2 size); + + /* Shorthand for acquire with GPU_RGBA16F format. Identical to acquire_color because vectors + * are stored in RGBA textures, due to the limited support for RGB textures. */ + GPUTexture *acquire_vector(int2 size); + + /* Shorthand for acquire with GPU_R16F format. */ + GPUTexture *acquire_float(int2 size); + + /* Put the texture back into the pool, potentially to be acquired later by another user. Expects + * the texture to be one that was acquired using the same texture pool. */ + void release(GPUTexture *texture); + + /* Reset the texture pool by clearing all available textures without freeing the textures. If the + * textures will no longer be needed, they should be freed in the destructor. This should be + * called after the compositor is done evaluating. */ + void reset(); + + private: + /* Returns a newly allocated texture with the given specification. This method should be + * implemented by the caller of the compositor evaluator. See the class description for more + * information. */ + virtual GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) = 0; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_utilities.hh b/source/blender/compositor/realtime_compositor/COM_utilities.hh new file mode 100644 index 00000000000..4bd61aab5cb --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_utilities.hh @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_function_ref.hh" +#include "BLI_math_vec_types.hh" + +#include "NOD_derived_node_tree.hh" + +#include "GPU_shader.h" + +#include "COM_input_descriptor.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* Get the origin socket of the given node input. If the input is not linked, the socket itself is + * returned. If the input is linked, the socket that is linked to it is returned, which could + * either be an input or an output. An input socket is returned when the given input is connected + * to an unlinked input of a group input node. */ +DSocket get_input_origin_socket(DInputSocket input); + +/* Get the output socket linked to the given node input. If the input is not linked to an output, a + * null output is returned. */ +DOutputSocket get_output_linked_to_input(DInputSocket input); + +/* Get the result type that corresponds to the type of the given socket. */ +ResultType get_node_socket_result_type(const SocketRef *socket); + +/* Returns true if any of the nodes linked to the given output satisfies the given condition, and + * false otherwise. */ +bool is_output_linked_to_node_conditioned(DOutputSocket output, + FunctionRef<bool(DNode)> condition); + +/* Returns the number of inputs linked to the given output that satisfy the given condition. */ +int number_of_inputs_linked_to_output_conditioned(DOutputSocket output, + FunctionRef<bool(DInputSocket)> condition); + +/* A node is a shader node if it defines a method to get a shader node operation. */ +bool is_shader_node(DNode node); + +/* Returns true if the given node is supported, that is, have an implementation. Returns false + * otherwise. */ +bool is_node_supported(DNode node); + +/* Get the input descriptor of the given input socket. */ +InputDescriptor input_descriptor_from_input_socket(const InputSocketRef *socket); + +/* Dispatch the given compute shader in a 2D compute space such that the number of threads in both + * dimensions is as small as possible but at least covers the entirety of threads_range assuming + * the shader has a local group size given by local_size. That means that the number of threads + * might be a bit larger than threads_range, so shaders has to put that into consideration. A + * default local size of 16x16 is assumed, which is the optimal local size for many image + * processing shaders. */ +void compute_dispatch_threads_at_least(GPUShader *shader, + int2 threads_range, + int2 local_size = int2(16)); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/compile_state.cc b/source/blender/compositor/realtime_compositor/intern/compile_state.cc new file mode 100644 index 00000000000..5b485224111 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/compile_state.cc @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <limits> + +#include "BLI_math_vec_types.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_node_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +CompileState::CompileState(const Schedule &schedule) : schedule_(schedule) +{ +} + +const Schedule &CompileState::get_schedule() +{ + return schedule_; +} + +void CompileState::map_node_to_node_operation(DNode node, NodeOperation *operations) +{ + return node_operations_.add_new(node, operations); +} + +void CompileState::map_node_to_shader_operation(DNode node, ShaderOperation *operations) +{ + return shader_operations_.add_new(node, operations); +} + +Result &CompileState::get_result_from_output_socket(DOutputSocket output) +{ + /* The output belongs to a node that was compiled into a standard node operation, so return a + * reference to the result from that operation using the output identifier. */ + if (node_operations_.contains(output.node())) { + NodeOperation *operation = node_operations_.lookup(output.node()); + return operation->get_result(output->identifier()); + } + + /* Otherwise, the output belongs to a node that was compiled into a shader operation, so + * retrieve the internal identifier of that output and return a reference to the result from + * that operation using the retrieved identifier. */ + ShaderOperation *operation = shader_operations_.lookup(output.node()); + return operation->get_result(operation->get_output_identifier_from_output_socket(output)); +} + +void CompileState::add_node_to_shader_compile_unit(DNode node) +{ + shader_compile_unit_.add_new(node); + + /* If the domain of the shader compile unit is not yet determined or was determined to be + * an identity domain, update it to be the computed domain of the node. */ + if (shader_compile_unit_domain_ == Domain::identity()) { + shader_compile_unit_domain_ = compute_shader_node_domain(node); + } +} + +ShaderCompileUnit &CompileState::get_shader_compile_unit() +{ + return shader_compile_unit_; +} + +void CompileState::reset_shader_compile_unit() +{ + return shader_compile_unit_.clear(); +} + +bool CompileState::should_compile_shader_compile_unit(DNode node) +{ + /* If the shader compile unit is empty, then it can't be compiled yet. */ + if (shader_compile_unit_.is_empty()) { + return false; + } + + /* If the node is not a shader node, then it can't be added to the shader compile unit and the + * shader compile unit is considered complete and should be compiled. */ + if (!is_shader_node(node)) { + return true; + } + + /* If the computed domain of the node doesn't matches the domain of the shader compile unit, then + * it can't be added to the shader compile unit and the shader compile unit is considered + * complete and should be compiled. Identity domains are an exception as they are always + * compatible because they represents single values. */ + if (shader_compile_unit_domain_ != Domain::identity() && + shader_compile_unit_domain_ != compute_shader_node_domain(node)) { + return true; + } + + /* Otherwise, the node is compatible and can be added to the compile unit and it shouldn't be + * compiled just yet. */ + return false; +} + +Domain CompileState::compute_shader_node_domain(DNode node) +{ + /* Default to an identity domain in case no domain input was found, most likely because all + * inputs are single values. */ + Domain node_domain = Domain::identity(); + int current_domain_priority = std::numeric_limits<int>::max(); + + /* Go over the inputs and find the domain of the non single value input with the highest domain + * priority. */ + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked, so skip + * it. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_ref); + + /* If the output belongs to a node that is part of the shader compile unit, then the domain of + * the input is the domain of the compile unit itself. */ + if (shader_compile_unit_.contains(output.node())) { + /* Single value inputs can't be domain inputs. */ + if (shader_compile_unit_domain_.size == int2(1)) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (input_descriptor.domain_priority < current_domain_priority) { + node_domain = shader_compile_unit_domain_; + current_domain_priority = input_descriptor.domain_priority; + } + continue; + } + + const Result &result = get_result_from_output_socket(output); + + /* A single value input can't be a domain input. */ + if (result.is_single_value() || input_descriptor.expects_single_value) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (input_descriptor.domain_priority < current_domain_priority) { + node_domain = result.domain(); + current_domain_priority = input_descriptor.domain_priority; + } + } + + return node_domain; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/context.cc b/source/blender/compositor/realtime_compositor/intern/context.cc new file mode 100644 index 00000000000..64ac29af3d1 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/context.cc @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "COM_context.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Context::Context(TexturePool &texture_pool) : texture_pool_(texture_pool) +{ +} + +int Context::get_frame_number() const +{ + return get_scene()->r.cfra; +} + +float Context::get_time() const +{ + const float frame_number = static_cast<float>(get_frame_number()); + const float frame_rate = static_cast<float>(get_scene()->r.frs_sec) / + static_cast<float>(get_scene()->r.frs_sec_base); + return frame_number / frame_rate; +} + +TexturePool &Context::texture_pool() +{ + return texture_pool_; +} + +StaticShaderManager &Context::shader_manager() +{ + return shader_manager_; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc new file mode 100644 index 00000000000..d6bf74ffbee --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_conversion_operation.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Conversion Operation. + */ + +void ConversionOperation::execute() +{ + Result &result = get_result(); + const Result &input = get_input(); + + if (input.is_single_value()) { + result.allocate_single_value(); + execute_single(input, result); + return; + } + + result.allocate_texture(input.domain()); + + GPUShader *shader = get_conversion_shader(); + GPU_shader_bind(shader); + + input.bind_as_texture(shader, "input_tx"); + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, input.domain().size); + + input.unbind_as_texture(); + result.unbind_as_image(); + GPU_shader_unbind(); +} + +SimpleOperation *ConversionOperation::construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor) +{ + ResultType result_type = input_result.type(); + ResultType expected_type = input_descriptor.type; + + /* If the result type differs from the expected type, return an instance of an appropriate + * conversion operation. Otherwise, return a null pointer. */ + + if (result_type == ResultType::Float && expected_type == ResultType::Vector) { + return new ConvertFloatToVectorOperation(context); + } + + if (result_type == ResultType::Float && expected_type == ResultType::Color) { + return new ConvertFloatToColorOperation(context); + } + + if (result_type == ResultType::Color && expected_type == ResultType::Float) { + return new ConvertColorToFloatOperation(context); + } + + if (result_type == ResultType::Color && expected_type == ResultType::Vector) { + return new ConvertColorToVectorOperation(context); + } + + if (result_type == ResultType::Vector && expected_type == ResultType::Float) { + return new ConvertVectorToFloatOperation(context); + } + + if (result_type == ResultType::Vector && expected_type == ResultType::Color) { + return new ConvertVectorToColorOperation(context); + } + + return nullptr; +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Float To Vector Operation. + */ + +ConvertFloatToVectorOperation::ConvertFloatToVectorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Float; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Vector, texture_pool())); +} + +void ConvertFloatToVectorOperation::execute_single(const Result &input, Result &output) +{ + output.set_vector_value(float3(input.get_float_value())); +} + +GPUShader *ConvertFloatToVectorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_float_to_vector"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Float To Color Operation. + */ + +ConvertFloatToColorOperation::ConvertFloatToColorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Float; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Color, texture_pool())); +} + +void ConvertFloatToColorOperation::execute_single(const Result &input, Result &output) +{ + float4 color = float4(input.get_float_value()); + color[3] = 1.0f; + output.set_color_value(color); +} + +GPUShader *ConvertFloatToColorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_float_to_color"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Color To Float Operation. + */ + +ConvertColorToFloatOperation::ConvertColorToFloatOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Color; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Float, texture_pool())); +} + +void ConvertColorToFloatOperation::execute_single(const Result &input, Result &output) +{ + float4 color = input.get_color_value(); + output.set_float_value((color[0] + color[1] + color[2]) / 3.0f); +} + +GPUShader *ConvertColorToFloatOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_color_to_float"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Color To Vector Operation. + */ + +ConvertColorToVectorOperation::ConvertColorToVectorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Color; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Vector, texture_pool())); +} + +void ConvertColorToVectorOperation::execute_single(const Result &input, Result &output) +{ + float4 color = input.get_color_value(); + output.set_vector_value(float3(color)); +} + +GPUShader *ConvertColorToVectorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_color_to_vector"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Vector To Float Operation. + */ + +ConvertVectorToFloatOperation::ConvertVectorToFloatOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Vector; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Float, texture_pool())); +} + +void ConvertVectorToFloatOperation::execute_single(const Result &input, Result &output) +{ + float3 vector = input.get_vector_value(); + output.set_float_value((vector[0] + vector[1] + vector[2]) / 3.0f); +} + +GPUShader *ConvertVectorToFloatOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_vector_to_float"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Vector To Color Operation. + */ + +ConvertVectorToColorOperation::ConvertVectorToColorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Vector; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Color, texture_pool())); +} + +void ConvertVectorToColorOperation::execute_single(const Result &input, Result &output) +{ + output.set_color_value(float4(input.get_vector_value(), 1.0f)); +} + +GPUShader *ConvertVectorToColorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_vector_to_color"); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/domain.cc b/source/blender/compositor/realtime_compositor/intern/domain.cc new file mode 100644 index 00000000000..31b297c212e --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/domain.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "COM_domain.hh" + +namespace blender::realtime_compositor { + +Domain::Domain(int2 size) : size(size), transformation(float3x3::identity()) +{ +} + +Domain::Domain(int2 size, float3x3 transformation) : size(size), transformation(transformation) +{ +} + +void Domain::transform(const float3x3 &input_transformation) +{ + transformation = input_transformation * transformation; +} + +Domain Domain::identity() +{ + return Domain(int2(1), float3x3::identity()); +} + +bool operator==(const Domain &a, const Domain &b) +{ + return a.size == b.size && a.transformation == b.transformation; +} + +bool operator!=(const Domain &a, const Domain &b) +{ + return !(a == b); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/evaluator.cc b/source/blender/compositor/realtime_compositor/intern/evaluator.cc new file mode 100644 index 00000000000..d358389f2e9 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/evaluator.cc @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <string> + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_context.hh" +#include "COM_evaluator.hh" +#include "COM_input_single_value_operation.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +Evaluator::Evaluator(Context &context, bNodeTree &node_tree) + : context_(context), node_tree_(node_tree) +{ +} + +void Evaluator::evaluate() +{ + context_.texture_pool().reset(); + + if (!is_compiled_) { + compile_and_evaluate(); + is_compiled_ = true; + return; + } + + for (const std::unique_ptr<Operation> &operation : operations_stream_) { + operation->evaluate(); + } +} + +void Evaluator::reset() +{ + operations_stream_.clear(); + derived_node_tree_.reset(); + node_tree_reference_map_.clear(); + + is_compiled_ = false; +} + +bool Evaluator::validate_node_tree() +{ + if (derived_node_tree_->has_link_cycles()) { + context_.set_info_message("Compositor node tree has cyclic links!"); + return false; + } + + if (derived_node_tree_->has_undefined_nodes_or_sockets()) { + context_.set_info_message("Compositor node tree has undefined nodes or sockets!"); + return false; + } + + return true; +} + +void Evaluator::compile_and_evaluate() +{ + derived_node_tree_ = std::make_unique<DerivedNodeTree>(node_tree_, node_tree_reference_map_); + + if (!validate_node_tree()) { + return; + } + + const Schedule schedule = compute_schedule(*derived_node_tree_); + + CompileState compile_state(schedule); + + for (const DNode &node : schedule) { + if (compile_state.should_compile_shader_compile_unit(node)) { + compile_and_evaluate_shader_compile_unit(compile_state); + } + + if (is_shader_node(node)) { + compile_state.add_node_to_shader_compile_unit(node); + } + else { + compile_and_evaluate_node(node, compile_state); + } + } +} + +void Evaluator::compile_and_evaluate_node(DNode node, CompileState &compile_state) +{ + NodeOperation *operation = node->typeinfo()->get_compositor_operation(context_, node); + + compile_state.map_node_to_node_operation(node, operation); + + map_node_operation_inputs_to_their_results(node, operation, compile_state); + + /* This has to be done after input mapping because the method may add Input Single Value + * Operations to the operations stream, which needs to be evaluated before the operation itself + * is evaluated. */ + operations_stream_.append(std::unique_ptr<Operation>(operation)); + + operation->compute_results_reference_counts(compile_state.get_schedule()); + + operation->evaluate(); +} + +void Evaluator::map_node_operation_inputs_to_their_results(DNode node, + NodeOperation *operation, + CompileState &compile_state) +{ + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + DSocket origin = get_input_origin_socket(input); + + /* The origin socket is an output, which means the input is linked. So map the input to the + * result we get from the output. */ + if (origin->is_output()) { + Result &result = compile_state.get_result_from_output_socket(DOutputSocket(origin)); + operation->map_input_to_result(input->identifier(), &result); + continue; + } + + /* Otherwise, the origin socket is an input, which either means the input is unlinked and the + * origin is the input socket itself or the input is connected to an unlinked input of a group + * input node and the origin is the input of the group input node. So map the input to the + * result of a newly created Input Single Value Operation. */ + auto *input_operation = new InputSingleValueOperation(context_, DInputSocket(origin)); + operation->map_input_to_result(input->identifier(), &input_operation->get_result()); + + operations_stream_.append(std::unique_ptr<InputSingleValueOperation>(input_operation)); + + input_operation->evaluate(); + } +} + +void Evaluator::compile_and_evaluate_shader_compile_unit(CompileState &compile_state) +{ + ShaderCompileUnit &compile_unit = compile_state.get_shader_compile_unit(); + ShaderOperation *operation = new ShaderOperation(context_, compile_unit); + + for (DNode node : compile_unit) { + compile_state.map_node_to_shader_operation(node, operation); + } + + map_shader_operation_inputs_to_their_results(operation, compile_state); + + operations_stream_.append(std::unique_ptr<Operation>(operation)); + + operation->compute_results_reference_counts(compile_state.get_schedule()); + + operation->evaluate(); + + compile_state.reset_shader_compile_unit(); +} + +void Evaluator::map_shader_operation_inputs_to_their_results(ShaderOperation *operation, + CompileState &compile_state) +{ + for (const auto &item : operation->get_inputs_to_linked_outputs_map().items()) { + Result &result = compile_state.get_result_from_output_socket(item.value); + operation->map_input_to_result(item.key, &result); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc new file mode 100644 index 00000000000..0bdd40e3636 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_math_vec_types.hh" + +#include "COM_input_single_value_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +const StringRef InputSingleValueOperation::output_identifier_ = StringRef("Output"); + +InputSingleValueOperation::InputSingleValueOperation(Context &context, DInputSocket input_socket) + : Operation(context), input_socket_(input_socket) +{ + const ResultType result_type = get_node_socket_result_type(input_socket_.socket_ref()); + Result result = Result(result_type, texture_pool()); + + /* The result of an input single value operation is guaranteed to have a single user. */ + result.set_initial_reference_count(1); + + populate_result(result); +} + +void InputSingleValueOperation::execute() +{ + /* Allocate a single value for the result. */ + Result &result = get_result(); + result.allocate_single_value(); + + /* Set the value of the result to the default value of the input socket. */ + switch (result.type()) { + case ResultType::Float: + result.set_float_value(input_socket_->default_value<bNodeSocketValueFloat>()->value); + break; + case ResultType::Vector: + result.set_vector_value( + float3(input_socket_->default_value<bNodeSocketValueVector>()->value)); + break; + case ResultType::Color: + result.set_color_value(float4(input_socket_->default_value<bNodeSocketValueRGBA>()->value)); + break; + } +} + +Result &InputSingleValueOperation::get_result() +{ + return Operation::get_result(output_identifier_); +} + +void InputSingleValueOperation::populate_result(Result result) +{ + Operation::populate_result(output_identifier_, result); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/node_operation.cc b/source/blender/compositor/realtime_compositor/intern/node_operation.cc new file mode 100644 index 00000000000..f02d0906447 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/node_operation.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +NodeOperation::NodeOperation(Context &context, DNode node) : Operation(context), node_(node) +{ + for (const OutputSocketRef *output : node->outputs()) { + const ResultType result_type = get_node_socket_result_type(output); + const Result result = Result(result_type, texture_pool()); + populate_result(output->identifier(), result); + } + + for (const InputSocketRef *input : node->inputs()) { + const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input); + declare_input_descriptor(input->identifier(), input_descriptor); + } +} + +void NodeOperation::compute_results_reference_counts(const Schedule &schedule) +{ + for (const OutputSocketRef *output_ref : node()->outputs()) { + const DOutputSocket output{node().context(), output_ref}; + + const int reference_count = number_of_inputs_linked_to_output_conditioned( + output, [&](DInputSocket input) { return schedule.contains(input.node()); }); + + get_result(output->identifier()).set_initial_reference_count(reference_count); + } +} + +const DNode &NodeOperation::node() const +{ + return node_; +} + +const bNode &NodeOperation::bnode() const +{ + return *node_->bnode(); +} + +bool NodeOperation::should_compute_output(StringRef identifier) +{ + return get_result(identifier).should_compute(); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/operation.cc b/source/blender/compositor/realtime_compositor/intern/operation.cc new file mode 100644 index 00000000000..42dd5aeebe8 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/operation.cc @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <limits> +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "COM_context.hh" +#include "COM_conversion_operation.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_operation.hh" +#include "COM_realize_on_domain_operation.hh" +#include "COM_reduce_to_single_value_operation.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Operation::Operation(Context &context) : context_(context) +{ +} + +Operation::~Operation() = default; + +void Operation::evaluate() +{ + evaluate_input_processors(); + + reset_results(); + + execute(); + + release_inputs(); +} + +Result &Operation::get_result(StringRef identifier) +{ + return results_.lookup(identifier); +} + +void Operation::map_input_to_result(StringRef identifier, Result *result) +{ + results_mapped_to_inputs_.add_new(identifier, result); +} + +Domain Operation::compute_domain() +{ + /* Default to an identity domain in case no domain input was found, most likely because all + * inputs are single values. */ + Domain operation_domain = Domain::identity(); + int current_domain_priority = std::numeric_limits<int>::max(); + + /* Go over the inputs and find the domain of the non single value input with the highest domain + * priority. */ + for (StringRef identifier : input_descriptors_.keys()) { + const Result &result = get_input(identifier); + const InputDescriptor &descriptor = get_input_descriptor(identifier); + + /* A single value input can't be a domain input. */ + if (result.is_single_value() || descriptor.expects_single_value) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (descriptor.domain_priority < current_domain_priority) { + operation_domain = result.domain(); + current_domain_priority = descriptor.domain_priority; + } + } + + return operation_domain; +} + +void Operation::add_and_evaluate_input_processors() +{ + /* Each input processor type is added to all inputs entirely before the next type. This is done + * because the construction of the input processors may depend on the result of previous input + * processors for all inputs. For instance, the realize on domain input processor considers the + * value of all inputs, so previous input processors for all inputs needs to be added and + * evaluated first. */ + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *single_value = ReduceToSingleValueOperation::construct_if_needed( + context(), get_input(identifier)); + add_and_evaluate_input_processor(identifier, single_value); + } + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *conversion = ConversionOperation::construct_if_needed( + context(), get_input(identifier), get_input_descriptor(identifier)); + add_and_evaluate_input_processor(identifier, conversion); + } + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed( + context(), get_input(identifier), get_input_descriptor(identifier), compute_domain()); + add_and_evaluate_input_processor(identifier, realize_on_domain); + } +} + +void Operation::add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor) +{ + /* Allow null inputs to facilitate construct_if_needed pattern of addition. For instance, see the + * implementation of the add_and_evaluate_input_processors method. */ + if (!processor) { + return; + } + + ProcessorsVector &processors = input_processors_.lookup_or_add_default(identifier); + + /* Get the result that should serve as the input for the processor. This is either the result + * mapped to the input or the result of the last processor depending on whether this is the first + * processor or not. */ + Result &result = processors.is_empty() ? get_input(identifier) : processors.last()->get_result(); + + /* Map the input result of the processor and add it to the processors vector. */ + processor->map_input_to_result(&result); + processors.append(std::unique_ptr<SimpleOperation>(processor)); + + /* Switch the result mapped to the input to be the output result of the processor. */ + switch_result_mapped_to_input(identifier, &processor->get_result()); + + processor->evaluate(); +} + +Result &Operation::get_input(StringRef identifier) const +{ + return *results_mapped_to_inputs_.lookup(identifier); +} + +void Operation::switch_result_mapped_to_input(StringRef identifier, Result *result) +{ + results_mapped_to_inputs_.lookup(identifier) = result; +} + +void Operation::populate_result(StringRef identifier, Result result) +{ + results_.add_new(identifier, result); +} + +void Operation::declare_input_descriptor(StringRef identifier, InputDescriptor descriptor) +{ + input_descriptors_.add_new(identifier, descriptor); +} + +InputDescriptor &Operation::get_input_descriptor(StringRef identifier) +{ + return input_descriptors_.lookup(identifier); +} + +Context &Operation::context() +{ + return context_; +} + +TexturePool &Operation::texture_pool() const +{ + return context_.texture_pool(); +} + +StaticShaderManager &Operation::shader_manager() const +{ + return context_.shader_manager(); +} + +void Operation::evaluate_input_processors() +{ + if (!input_processors_added_) { + add_and_evaluate_input_processors(); + input_processors_added_ = true; + return; + } + + for (const ProcessorsVector &processors : input_processors_.values()) { + for (const std::unique_ptr<SimpleOperation> &processor : processors) { + processor->evaluate(); + } + } +} + +void Operation::reset_results() +{ + for (Result &result : results_.values()) { + result.reset(); + } +} + +void Operation::release_inputs() +{ + for (Result *result : results_mapped_to_inputs_.values()) { + result->release(); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc new file mode 100644 index 00000000000..47993060a74 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_utildefines.h" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_realize_on_domain_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context, + Domain domain, + ResultType type) + : SimpleOperation(context), domain_(domain) +{ + InputDescriptor input_descriptor; + input_descriptor.type = type; + declare_input_descriptor(input_descriptor); + populate_result(Result(type, texture_pool())); +} + +void RealizeOnDomainOperation::execute() +{ + Result &input = get_input(); + Result &result = get_result(); + + result.allocate_texture(domain_); + + GPUShader *shader = get_realization_shader(); + GPU_shader_bind(shader); + + /* Transform the input space into the domain space. */ + const float3x3 local_transformation = input.domain().transformation * + domain_.transformation.inverted(); + + /* Set the origin of the transformation to be the center of the domain. */ + const float3x3 transformation = float3x3::from_origin_transformation( + local_transformation, float2(domain_.size) / 2.0f); + + /* Invert the transformation because the shader transforms the domain coordinates instead of the + * input image itself and thus expect the inverse. */ + const float3x3 inverse_transformation = transformation.inverted(); + + GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr()); + + /* The texture sampler should use bilinear interpolation for both the bilinear and bicubic + * cases, as the logic used by the bicubic realization shader expects textures to use bilinear + * interpolation. */ + const bool use_bilinear = ELEM(input.get_realization_options().interpolation, + Interpolation::Bilinear, + Interpolation::Bicubic); + GPU_texture_filter_mode(input.texture(), use_bilinear); + + /* Make out-of-bound texture access return zero by clamping to border color. And make texture + * wrap appropriately if the input repeats. */ + const bool repeats = input.get_realization_options().repeat_x || + input.get_realization_options().repeat_y; + GPU_texture_wrap_mode(input.texture(), repeats, false); + + input.bind_as_texture(shader, "input_tx"); + result.bind_as_image(shader, "domain_img"); + + compute_dispatch_threads_at_least(shader, domain_.size); + + input.unbind_as_texture(); + result.unbind_as_image(); + GPU_shader_unbind(); +} + +GPUShader *RealizeOnDomainOperation::get_realization_shader() +{ + switch (get_result().type()) { + case ResultType::Color: + return shader_manager().get("compositor_realize_on_domain_color"); + case ResultType::Vector: + return shader_manager().get("compositor_realize_on_domain_vector"); + case ResultType::Float: + return shader_manager().get("compositor_realize_on_domain_float"); + } + + BLI_assert_unreachable(); + return nullptr; +} + +Domain RealizeOnDomainOperation::compute_domain() +{ + return domain_; +} + +SimpleOperation *RealizeOnDomainOperation::construct_if_needed( + Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor, + const Domain &operation_domain) +{ + /* This input wants to skip realization, the operation is not needed. */ + if (input_descriptor.skip_realization) { + return nullptr; + } + + /* The input expects a single value and if no single value is provided, it will be ignored and a + * default value will be used, so no need to realize it and the operation is not needed. */ + if (input_descriptor.expects_single_value) { + return nullptr; + } + + /* Input result is a single value and does not need realization, the operation is not needed. */ + if (input_result.is_single_value()) { + return nullptr; + } + + /* The input have an identical domain to the operation domain, so no need to realize it and the + * operation is not needed. */ + if (input_result.domain() == operation_domain) { + return nullptr; + } + + /* Otherwise, realization is needed. */ + return new RealizeOnDomainOperation(context, operation_domain, input_descriptor.type); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc new file mode 100644 index 00000000000..acc9b4ab7d6 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "MEM_guardedalloc.h" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_reduce_to_single_value_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +ReduceToSingleValueOperation::ReduceToSingleValueOperation(Context &context, ResultType type) + : SimpleOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = type; + declare_input_descriptor(input_descriptor); + populate_result(Result(type, texture_pool())); +} + +void ReduceToSingleValueOperation::execute() +{ + /* Make sure any prior writes to the texture are reflected before downloading it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + const Result &input = get_input(); + float *pixel = static_cast<float *>(GPU_texture_read(input.texture(), GPU_DATA_FLOAT, 0)); + + Result &result = get_result(); + result.allocate_single_value(); + switch (result.type()) { + case ResultType::Color: + result.set_color_value(pixel); + break; + case ResultType::Vector: + result.set_vector_value(pixel); + break; + case ResultType::Float: + result.set_float_value(*pixel); + break; + } + + MEM_freeN(pixel); +} + +SimpleOperation *ReduceToSingleValueOperation::construct_if_needed(Context &context, + const Result &input_result) +{ + /* Input result is already a single value, the operation is not needed. */ + if (input_result.is_single_value()) { + return nullptr; + } + + /* The input is a full sized texture and can't be reduced to a single value, the operation is not + * needed. */ + if (input_result.domain().size != int2(1)) { + return nullptr; + } + + /* The input is a texture of a single pixel and can be reduced to a single value. */ + return new ReduceToSingleValueOperation(context, input_result.type()); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/result.cc b/source/blender/compositor/realtime_compositor/intern/result.cc new file mode 100644 index 00000000000..8059367d211 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/result.cc @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_domain.hh" +#include "COM_result.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Result::Result(ResultType type, TexturePool &texture_pool) + : type_(type), texture_pool_(&texture_pool) +{ +} + +void Result::allocate_texture(Domain domain) +{ + is_single_value_ = false; + switch (type_) { + case ResultType::Float: + texture_ = texture_pool_->acquire_float(domain.size); + break; + case ResultType::Vector: + texture_ = texture_pool_->acquire_vector(domain.size); + break; + case ResultType::Color: + texture_ = texture_pool_->acquire_color(domain.size); + break; + } + domain_ = domain; +} + +void Result::allocate_single_value() +{ + is_single_value_ = true; + /* Single values are stored in 1x1 textures as well as the single value members. */ + const int2 texture_size{1, 1}; + switch (type_) { + case ResultType::Float: + texture_ = texture_pool_->acquire_float(texture_size); + break; + case ResultType::Vector: + texture_ = texture_pool_->acquire_vector(texture_size); + break; + case ResultType::Color: + texture_ = texture_pool_->acquire_color(texture_size); + break; + } + domain_ = Domain::identity(); +} + +void Result::allocate_invalid() +{ + allocate_single_value(); + switch (type_) { + case ResultType::Float: + set_float_value(0.0f); + break; + case ResultType::Vector: + set_vector_value(float3(0.0f)); + break; + case ResultType::Color: + set_color_value(float4(0.0f)); + break; + } +} + +void Result::bind_as_texture(GPUShader *shader, const char *texture_name) const +{ + /* Make sure any prior writes to the texture are reflected before reading from it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); +} + +void Result::bind_as_image(GPUShader *shader, const char *image_name) const +{ + const int image_unit = GPU_shader_get_texture_binding(shader, image_name); + GPU_texture_image_bind(texture_, image_unit); +} + +void Result::unbind_as_texture() const +{ + GPU_texture_unbind(texture_); +} + +void Result::unbind_as_image() const +{ + GPU_texture_image_unbind(texture_); +} + +void Result::pass_through(Result &target) +{ + /* Increment the reference count of the master by the original reference count of the target. */ + increment_reference_count(target.reference_count()); + + /* Make the target an exact copy of this result, but keep the initial reference count, as this is + * a property of the original result and is needed for correctly resetting the result before the + * next evaluation. */ + const int initial_reference_count = target.initial_reference_count_; + target = *this; + target.initial_reference_count_ = initial_reference_count; + + target.master_ = this; +} + +void Result::transform(const float3x3 &transformation) +{ + domain_.transform(transformation); +} + +RealizationOptions &Result::get_realization_options() +{ + return domain_.realization_options; +} + +float Result::get_float_value() const +{ + return float_value_; +} + +float3 Result::get_vector_value() const +{ + return vector_value_; +} + +float4 Result::get_color_value() const +{ + return color_value_; +} + +float Result::get_float_value_default(float default_value) const +{ + if (is_single_value()) { + return get_float_value(); + } + return default_value; +} + +float3 Result::get_vector_value_default(const float3 &default_value) const +{ + if (is_single_value()) { + return get_vector_value(); + } + return default_value; +} + +float4 Result::get_color_value_default(const float4 &default_value) const +{ + if (is_single_value()) { + return get_color_value(); + } + return default_value; +} + +void Result::set_float_value(float value) +{ + float_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, &float_value_); +} + +void Result::set_vector_value(const float3 &value) +{ + vector_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, vector_value_); +} + +void Result::set_color_value(const float4 &value) +{ + color_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, color_value_); +} + +void Result::set_initial_reference_count(int count) +{ + initial_reference_count_ = count; +} + +void Result::reset() +{ + master_ = nullptr; + reference_count_ = initial_reference_count_; +} + +void Result::increment_reference_count(int count) +{ + /* If there is a master result, increment its reference count instead. */ + if (master_) { + master_->increment_reference_count(count); + return; + } + + reference_count_ += count; +} + +void Result::release() +{ + /* If there is a master result, release it instead. */ + if (master_) { + master_->release(); + return; + } + + /* Decrement the reference count, and if it reaches zero, release the texture back into the + * texture pool. */ + reference_count_--; + if (reference_count_ == 0) { + texture_pool_->release(texture_); + } +} + +bool Result::should_compute() +{ + return initial_reference_count_ != 0; +} + +ResultType Result::type() const +{ + return type_; +} + +bool Result::is_texture() const +{ + return !is_single_value_; +} + +bool Result::is_single_value() const +{ + return is_single_value_; +} + +GPUTexture *Result::texture() const +{ + return texture_; +} + +int Result::reference_count() const +{ + /* If there is a master result, return its reference count instead. */ + if (master_) { + return master_->reference_count(); + } + return reference_count_; +} + +const Domain &Result::domain() const +{ + return domain_; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/scheduler.cc b/source/blender/compositor/realtime_compositor/intern/scheduler.cc new file mode 100644 index 00000000000..ce8b9330541 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/scheduler.cc @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_scheduler.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* Compute the output node whose result should be computed. The output node is the node marked as + * NODE_DO_OUTPUT. If multiple types of output nodes are marked, then the preference will be + * CMP_NODE_COMPOSITE > CMP_NODE_VIEWER > CMP_NODE_SPLITVIEWER. If no output node exists, a null + * node will be returned. */ +static DNode compute_output_node(DerivedNodeTree &tree) +{ + const NodeTreeRef &root_tree = tree.root_context().tree(); + + for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeComposite")) { + if (node->bnode()->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeViewer")) { + if (node->bnode()->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeSplitViewer")) { + if (node->bnode()->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + /* No output node found, return a null node. */ + return DNode(); +} + +/* A type representing a mapping that associates each node with a heuristic estimation of the + * number of intermediate buffers needed to compute it and all of its dependencies. See the + * compute_number_of_needed_buffers function for more information. */ +using NeededBuffers = Map<DNode, int>; + +/* Compute a heuristic estimation of the number of intermediate buffers needed to compute each node + * and all of its dependencies for all nodes that the given node depends on. The output is a map + * that maps each node with the number of intermediate buffers needed to compute it and all of its + * dependencies. + * + * Consider a node that takes n number of buffers as an input from a number of node dependencies, + * which we shall call the input nodes. The node also computes and outputs m number of buffers. + * In order for the node to compute its output, a number of intermediate buffers will be needed. + * Since the node takes n buffers and outputs m buffers, then the number of buffers directly + * needed by the node is (n + m). But each of the input buffers are computed by a node that, in + * turn, needs a number of buffers to compute its output. So the total number of buffers needed + * to compute the output of the node is max(n + m, d) where d is the number of buffers needed by + * the input node that needs the largest number of buffers. We only consider the input node that + * needs the largest number of buffers, because those buffers can be reused by any input node + * that needs a lesser number of buffers. + * + * Shader nodes, however, are a special case because links between two shader nodes inside the same + * shader operation don't pass a buffer, but a single value in the compiled shader. So for shader + * nodes, only inputs and outputs linked to nodes that are not shader nodes should be considered. + * Note that this might not actually be true, because the compiler may decide to split a shader + * operation into multiples ones that will pass buffers, but this is not something that can be + * known at scheduling-time. See the discussion in COM_compile_state.hh, COM_evaluator.hh, and + * COM_shader_operation.hh for more information. In the node tree shown below, node 4 will have + * exactly the same number of needed buffers by node 3, because its inputs and outputs are all + * internally linked in the shader operation. + * + * Shader Operation + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * Note that the computed output is not guaranteed to be accurate, and will not be in most cases. + * The computation is merely a heuristic estimation that works well in most cases. This is due to a + * number of reasons: + * - The node tree is actually a graph that allows output sharing, which is not something that was + * taken into consideration in this implementation because it is difficult to correctly consider. + * - Each node may allocate any number of internal buffers, which is not taken into account in this + * implementation because it rarely affects the output and is done by very few nodes. + * - The compiler may decide to compiler the schedule differently depending on runtime information + * which we can merely speculate at scheduling-time as described above. */ +static NeededBuffers compute_number_of_needed_buffers(DNode output_node) +{ + NeededBuffers needed_buffers; + + /* A stack of nodes used to traverse the node tree starting from the output node. */ + Stack<DNode> node_stack = {output_node}; + + /* Traverse the node tree in a post order depth first manner and compute the number of needed + * buffers for each node. Post order traversal guarantee that all the node dependencies of each + * node are computed before it. This is done by pushing all the uncomputed node dependencies to + * the node stack first and only popping and computing the node when all its node dependencies + * were computed. */ + while (!node_stack.is_empty()) { + /* Do not pop the node immediately, as it may turn out that we can't compute its number of + * needed buffers just yet because its dependencies weren't computed, it will be popped later + * when needed. */ + DNode &node = node_stack.peek(); + + /* Go over the node dependencies connected to the inputs of the node and push them to the node + * stack if they were not computed already. */ + Set<DNode> pushed_nodes; + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked and + * has no dependency node. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + /* The node dependency was already computed or pushed before, so skip it. */ + if (needed_buffers.contains(output.node()) || pushed_nodes.contains(output.node())) { + continue; + } + + /* The output node needs to be computed, push the node dependency to the node stack and + * indicate that it was pushed. */ + node_stack.push(output.node()); + pushed_nodes.add_new(output.node()); + } + + /* If any of the node dependencies were pushed, that means that not all of them were computed + * and consequently we can't compute the number of needed buffers for this node just yet. */ + if (!pushed_nodes.is_empty()) { + continue; + } + + /* We don't need to store the result of the pop because we already peeked at it before. */ + node_stack.pop(); + + /* Compute the number of buffers that the node takes as an input as well as the number of + * buffers needed to compute the most demanding of the node dependencies. */ + int number_of_input_buffers = 0; + int buffers_needed_by_dependencies = 0; + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked. + * Unlinked inputs do not take a buffer, so skip those inputs. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + /* Since this input is linked, if the link is not between two shader nodes, it means that the + * node takes a buffer through this input and so we increment the number of input buffers. */ + if (!is_shader_node(node) || !is_shader_node(output.node())) { + number_of_input_buffers++; + } + + /* If the number of buffers needed by the node dependency is more than the total number of + * buffers needed by the dependencies, then update the latter to be the former. This is + * computing the "d" in the aforementioned equation "max(n + m, d)". */ + const int buffers_needed_by_dependency = needed_buffers.lookup(output.node()); + if (buffers_needed_by_dependency > buffers_needed_by_dependencies) { + buffers_needed_by_dependencies = buffers_needed_by_dependency; + } + } + + /* Compute the number of buffers that will be computed/output by this node. */ + int number_of_output_buffers = 0; + for (const OutputSocketRef *output_ref : node->outputs()) { + const DOutputSocket output{node.context(), output_ref}; + + /* The output is not linked, it outputs no buffer. */ + if (output->logically_linked_sockets().is_empty()) { + continue; + } + + /* If any of the links is not between two shader nodes, it means that the node outputs + * a buffer through this output and so we increment the number of output buffers. */ + if (!is_output_linked_to_node_conditioned(output, is_shader_node) || !is_shader_node(node)) { + number_of_output_buffers++; + } + } + + /* Compute the heuristic estimation of the number of needed intermediate buffers to compute + * this node and all of its dependencies. This is computing the aforementioned equation + * "max(n + m, d)". */ + const int total_buffers = MAX2(number_of_input_buffers + number_of_output_buffers, + buffers_needed_by_dependencies); + needed_buffers.add(node, total_buffers); + } + + return needed_buffers; +} + +/* There are multiple different possible orders of evaluating a node graph, each of which needs + * to allocate a number of intermediate buffers to store its intermediate results. It follows + * that we need to find the evaluation order which uses the least amount of intermediate buffers. + * For instance, consider a node that takes two input buffers A and B. Each of those buffers is + * computed through a number of nodes constituting a sub-graph whose root is the node that + * outputs that buffer. Suppose the number of intermediate buffers needed to compute A and B are + * N(A) and N(B) respectively and N(A) > N(B). Then evaluating the sub-graph computing A would be + * a better option than that of B, because had B was computed first, its outputs will need to be + * stored in extra buffers in addition to the buffers needed by A. The number of buffers needed by + * each node is estimated as described in the compute_number_of_needed_buffers function. + * + * This is a heuristic generalization of the Sethi–Ullman algorithm, a generalization that + * doesn't always guarantee an optimal evaluation order, as the optimal evaluation order is very + * difficult to compute, however, this method works well in most cases. Moreover it assumes that + * all buffers will have roughly the same size, which may not always be the case. */ +Schedule compute_schedule(DerivedNodeTree &tree) +{ + Schedule schedule; + + /* Compute the output node whose result should be computed. */ + const DNode output_node = compute_output_node(tree); + + /* No output node, the node tree has no effect, return an empty schedule. */ + if (!output_node) { + return schedule; + } + + /* Compute the number of buffers needed by each node connected to the output. */ + const NeededBuffers needed_buffers = compute_number_of_needed_buffers(output_node); + + /* A stack of nodes used to traverse the node tree starting from the output node. */ + Stack<DNode> node_stack = {output_node}; + + /* Traverse the node tree in a post order depth first manner, scheduling the nodes in an order + * informed by the number of buffers needed by each node. Post order traversal guarantee that all + * the node dependencies of each node are scheduled before it. This is done by pushing all the + * unscheduled node dependencies to the node stack first and only popping and scheduling the node + * when all its node dependencies were scheduled. */ + while (!node_stack.is_empty()) { + /* Do not pop the node immediately, as it may turn out that we can't schedule it just yet + * because its dependencies weren't scheduled, it will be popped later when needed. */ + DNode &node = node_stack.peek(); + + /* Compute the nodes directly connected to the node inputs sorted by their needed buffers such + * that the node with the lowest number of needed buffers comes first. Note that we actually + * want the node with the highest number of needed buffers to be schedule first, but since + * those are pushed to the traversal stack, we need to push them in reverse order. */ + Vector<DNode> sorted_dependency_nodes; + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked and + * has no dependency node, so skip it. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + /* The dependency node was added before, so skip it. The number of dependency nodes is very + * small, typically less than 3, so a linear search is okay. */ + if (sorted_dependency_nodes.contains(output.node())) { + continue; + } + + /* The dependency node was already schedule, so skip it. */ + if (schedule.contains(output.node())) { + continue; + } + + /* Sort in ascending order on insertion, the number of dependency nodes is very small, + * typically less than 3, so insertion sort is okay. */ + int insertion_position = 0; + for (int i = 0; i < sorted_dependency_nodes.size(); i++) { + if (needed_buffers.lookup(output.node()) > + needed_buffers.lookup(sorted_dependency_nodes[i])) { + insertion_position++; + } + else { + break; + } + } + sorted_dependency_nodes.insert(insertion_position, output.node()); + } + + /* Push the sorted dependency nodes to the node stack in order. */ + for (const DNode &dependency_node : sorted_dependency_nodes) { + node_stack.push(dependency_node); + } + + /* If there are no sorted dependency nodes, that means they were all already scheduled or that + * none exists in the first place, so we can pop and schedule the node now. */ + if (sorted_dependency_nodes.is_empty()) { + /* The node might have already been scheduled, so we don't use add_new here and simply don't + * add it if it was already scheduled. */ + schedule.add(node_stack.pop()); + } + } + + return schedule; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/shader_node.cc b/source/blender/compositor/realtime_compositor/intern/shader_node.cc new file mode 100644 index 00000000000..f23485cee96 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/shader_node.cc @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_assert.h" +#include "BLI_math_vector.h" +#include "BLI_string_ref.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +ShaderNode::ShaderNode(DNode node) : node_(node) +{ + populate_inputs(); + populate_outputs(); +} + +GPUNodeStack *ShaderNode::get_inputs_array() +{ + return inputs_.data(); +} + +GPUNodeStack *ShaderNode::get_outputs_array() +{ + return outputs_.data(); +} + +GPUNodeStack &ShaderNode::get_input(StringRef identifier) +{ + return inputs_[node_.input_by_identifier(identifier)->index()]; +} + +GPUNodeStack &ShaderNode::get_output(StringRef identifier) +{ + return outputs_[node_.output_by_identifier(identifier)->index()]; +} + +GPUNodeLink *ShaderNode::get_input_link(StringRef identifier) +{ + GPUNodeStack &input = get_input(identifier); + if (input.link) { + return input.link; + } + return GPU_uniform(input.vec); +} + +const DNode &ShaderNode::node() const +{ + return node_; +} + +bNode &ShaderNode::bnode() const +{ + return *node_->bnode(); +} + +static eGPUType gpu_type_from_socket_type(eNodeSocketDatatype type) +{ + switch (type) { + case SOCK_FLOAT: + return GPU_FLOAT; + case SOCK_VECTOR: + return GPU_VEC3; + case SOCK_RGBA: + return GPU_VEC4; + default: + BLI_assert_unreachable(); + return GPU_NONE; + } +} + +static void gpu_stack_vector_from_socket(float *vector, const SocketRef *socket) +{ + switch (socket->bsocket()->type) { + case SOCK_FLOAT: + vector[0] = socket->default_value<bNodeSocketValueFloat>()->value; + return; + case SOCK_VECTOR: + copy_v3_v3(vector, socket->default_value<bNodeSocketValueVector>()->value); + return; + case SOCK_RGBA: + copy_v4_v4(vector, socket->default_value<bNodeSocketValueRGBA>()->value); + return; + default: + BLI_assert_unreachable(); + } +} + +static void populate_gpu_node_stack(DSocket socket, GPUNodeStack &stack) +{ + /* Make sure this stack is not marked as the end of the stack array. */ + stack.end = false; + /* This will be initialized later by the GPU material compiler or the compile method. */ + stack.link = nullptr; + + stack.sockettype = socket->bsocket()->type; + stack.type = gpu_type_from_socket_type((eNodeSocketDatatype)socket->bsocket()->type); + + if (socket->is_input()) { + const DInputSocket input(socket); + + DSocket origin = get_input_origin_socket(input); + + /* The input is linked if the origin socket is an output socket. Had it been an input socket, + * then it is an unlinked input of a group input node. */ + stack.hasinput = origin->is_output(); + + /* Get the socket value from the origin if it is an input, because then it would either be an + * unlinked input or an unlinked input of a group input node that the socket is linked to, + * otherwise, get the value from the socket itself. */ + if (origin->is_input()) { + gpu_stack_vector_from_socket(stack.vec, origin.socket_ref()); + } + else { + gpu_stack_vector_from_socket(stack.vec, socket.socket_ref()); + } + } + else { + stack.hasoutput = socket->is_logically_linked(); + } +} + +void ShaderNode::populate_inputs() +{ + /* Reserve a stack for each input in addition to an extra stack at the end to mark the end of the + * array, as this is what the GPU module functions expect. */ + inputs_.resize(node_->inputs().size() + 1); + inputs_.last().end = true; + + for (int i = 0; i < node_->inputs().size(); i++) { + populate_gpu_node_stack(node_.input(i), inputs_[i]); + } +} + +void ShaderNode::populate_outputs() +{ + /* Reserve a stack for each output in addition to an extra stack at the end to mark the end of + * the array, as this is what the GPU module functions expect. */ + outputs_.resize(node_->outputs().size() + 1); + outputs_.last().end = true; + + for (int i = 0; i < node_->outputs().size(); i++) { + populate_gpu_node_stack(node_.output(i), outputs_[i]); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/shader_operation.cc b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc new file mode 100644 index 00000000000..a097c81a4c5 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc @@ -0,0 +1,522 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <memory> +#include <string> + +#include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_utildefines.h" + +#include "DNA_customdata_types.h" + +#include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_uniform_buffer.h" + +#include "gpu_shader_create_info.hh" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_node.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +ShaderOperation::ShaderOperation(Context &context, ShaderCompileUnit &compile_unit) + : Operation(context), compile_unit_(compile_unit) +{ + material_ = GPU_material_from_callbacks(&construct_material, &generate_code, this); + GPU_material_status_set(material_, GPU_MAT_QUEUED); + GPU_material_compile(material_); +} + +ShaderOperation::~ShaderOperation() +{ + GPU_material_free_single(material_); +} + +void ShaderOperation::execute() +{ + const Domain domain = compute_domain(); + for (StringRef identifier : output_sockets_to_output_identifiers_map_.values()) { + Result &result = get_result(identifier); + result.allocate_texture(domain); + } + + GPUShader *shader = GPU_material_get_shader(material_); + GPU_shader_bind(shader); + + bind_material_resources(shader); + bind_inputs(shader); + bind_outputs(shader); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_texture_unbind_all(); + GPU_texture_image_unbind_all(); + GPU_uniformbuf_unbind_all(); + GPU_shader_unbind(); +} + +StringRef ShaderOperation::get_output_identifier_from_output_socket(DOutputSocket output_socket) +{ + return output_sockets_to_output_identifiers_map_.lookup(output_socket); +} + +Map<std::string, DOutputSocket> &ShaderOperation::get_inputs_to_linked_outputs_map() +{ + return inputs_to_linked_outputs_map_; +} + +void ShaderOperation::compute_results_reference_counts(const Schedule &schedule) +{ + for (const auto &item : output_sockets_to_output_identifiers_map_.items()) { + const int reference_count = number_of_inputs_linked_to_output_conditioned( + item.key, [&](DInputSocket input) { return schedule.contains(input.node()); }); + + get_result(item.value).set_initial_reference_count(reference_count); + } +} + +void ShaderOperation::bind_material_resources(GPUShader *shader) +{ + /* Bind the uniform buffer of the material if it exists. It may not exist if the GPU material has + * no uniforms. */ + GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material_); + if (ubo) { + GPU_uniformbuf_bind(ubo, GPU_shader_get_uniform_block_binding(shader, GPU_UBO_BLOCK_NAME)); + } + + /* Bind color band textures needed by curve and ramp nodes. */ + ListBase textures = GPU_material_textures(material_); + LISTBASE_FOREACH (GPUMaterialTexture *, texture, &textures) { + if (texture->colorband) { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture->sampler_name); + GPU_texture_bind(*texture->colorband, texture_image_unit); + } + } +} + +void ShaderOperation::bind_inputs(GPUShader *shader) +{ + /* Attributes represents the inputs of the operation and their names match those of the inputs of + * the operation as well as the corresponding texture samples in the shader. */ + ListBase attributes = GPU_material_attributes(material_); + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + get_input(attribute->name).bind_as_texture(shader, attribute->name); + } +} + +void ShaderOperation::bind_outputs(GPUShader *shader) +{ + for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) { + get_result(output_identifier).bind_as_image(shader, output_identifier.c_str()); + } +} + +void ShaderOperation::construct_material(void *thunk, GPUMaterial *material) +{ + ShaderOperation *operation = static_cast<ShaderOperation *>(thunk); + for (DNode node : operation->compile_unit_) { + ShaderNode *shader_node = node->typeinfo()->get_compositor_shader_node(node); + operation->shader_nodes_.add_new(node, std::unique_ptr<ShaderNode>(shader_node)); + + operation->link_node_inputs(node, material); + + shader_node->compile(material); + + operation->populate_results_for_node(node, material); + } +} + +void ShaderOperation::link_node_inputs(DNode node, GPUMaterial *material) +{ + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked. + * Unlinked inputs are linked by the node compile method, so skip this here. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + /* If the origin node is part of the shader operation, then the link is internal to the GPU + * material graph and is linked appropriately. */ + if (compile_unit_.contains(output.node())) { + link_node_input_internal(input, output); + continue; + } + + /* Otherwise, the origin node is not part of the shader operation, then the link is external to + * the GPU material graph and an input to the shader operation must be declared and linked to + * the node input. */ + link_node_input_external(input, output, material); + } +} + +void ShaderOperation::link_node_input_internal(DInputSocket input_socket, + DOutputSocket output_socket) +{ + ShaderNode &output_node = *shader_nodes_.lookup(output_socket.node()); + GPUNodeStack &output_stack = output_node.get_output(output_socket->identifier()); + + ShaderNode &input_node = *shader_nodes_.lookup(input_socket.node()); + GPUNodeStack &input_stack = input_node.get_input(input_socket->identifier()); + + input_stack.link = output_stack.link; +} + +void ShaderOperation::link_node_input_external(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material) +{ + + ShaderNode &node = *shader_nodes_.lookup(input_socket.node()); + GPUNodeStack &stack = node.get_input(input_socket->identifier()); + + /* An input was already declared for that same output socket, so no need to declare it again. */ + if (!output_to_material_attribute_map_.contains(output_socket)) { + declare_operation_input(input_socket, output_socket, material); + } + + /* Link the attribute representing the shader operation input corresponding to the given output + * socket. */ + stack.link = output_to_material_attribute_map_.lookup(output_socket); +} + +static const char *get_set_function_name(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "set_value"; + case ResultType::Vector: + return "set_rgb"; + case ResultType::Color: + return "set_rgba"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::declare_operation_input(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material) +{ + const int input_index = output_to_material_attribute_map_.size(); + std::string input_identifier = "input" + std::to_string(input_index); + + /* Declare the input descriptor for this input and prefer to declare its type to be the same as + * the type of the output socket because doing type conversion in the shader is much cheaper. */ + InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_socket.socket_ref()); + input_descriptor.type = get_node_socket_result_type(output_socket.socket_ref()); + declare_input_descriptor(input_identifier, input_descriptor); + + /* Add a new GPU attribute representing an input to the GPU material. Instead of using the + * attribute directly, we link it to an appropriate set function and use its output link instead. + * This is needed because the `gputype` member of the attribute is only initialized if it is + * linked to a GPU node. */ + GPUNodeLink *attribute_link; + GPU_link(material, + get_set_function_name(input_descriptor.type), + GPU_attribute(material, CD_AUTO_FROM_NAME, input_identifier.c_str()), + &attribute_link); + + /* Map the output socket to the attribute that was created for it. */ + output_to_material_attribute_map_.add(output_socket, attribute_link); + + /* Map the identifier of the operation input to the output socket it is linked to. */ + inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket); +} + +void ShaderOperation::populate_results_for_node(DNode node, GPUMaterial *material) +{ + for (const OutputSocketRef *output_ref : node->outputs()) { + const DOutputSocket output{node.context(), output_ref}; + + /* If any of the nodes linked to the output are not part of the shader operation, then an + * output result needs to be populated for it. */ + const bool need_to_populate_result = is_output_linked_to_node_conditioned( + output, [&](DNode node) { return !compile_unit_.contains(node); }); + + if (need_to_populate_result) { + populate_operation_result(output, material); + } + } +} + +static const char *get_store_function_name(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "node_compositor_store_output_float"; + case ResultType::Vector: + return "node_compositor_store_output_vector"; + case ResultType::Color: + return "node_compositor_store_output_color"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::populate_operation_result(DOutputSocket output_socket, GPUMaterial *material) +{ + const unsigned int output_id = output_sockets_to_output_identifiers_map_.size(); + std::string output_identifier = "output" + std::to_string(output_id); + + const ResultType result_type = get_node_socket_result_type(output_socket.socket_ref()); + const Result result = Result(result_type, texture_pool()); + populate_result(output_identifier, result); + + /* Map the output socket to the identifier of the newly populated result. */ + output_sockets_to_output_identifiers_map_.add_new(output_socket, output_identifier); + + ShaderNode &node = *shader_nodes_.lookup(output_socket.node()); + GPUNodeLink *output_link = node.get_output(output_socket->identifier()).link; + + /* Link the output node stack to an output storer storing in the appropriate result. The result + * is identified by its index in the operation and the index is encoded as a float to be passed + * to the GPU function. Additionally, create an output link from the storer node to declare as an + * output to the GPU material. This storer output link is a dummy link in the sense that its + * value is ignored since it is already written in the output, but it is used to track nodes that + * contribute to the output of the compositor node tree. */ + GPUNodeLink *storer_output_link; + GPUNodeLink *id_link = GPU_constant((float *)&output_id); + const char *store_function_name = get_store_function_name(result_type); + GPU_link(material, store_function_name, id_link, output_link, &storer_output_link); + + /* Declare the output link of the storer node as an output of the GPU material to help the GPU + * code generator to track the nodes that contribute to the output of the shader. */ + GPU_material_add_output_link_composite(material, storer_output_link); +} + +using namespace gpu::shader; + +void ShaderOperation::generate_code(void *thunk, + GPUMaterial *material, + GPUCodegenOutput *code_generator_output) +{ + ShaderOperation *operation = static_cast<ShaderOperation *>(thunk); + ShaderCreateInfo &shader_create_info = *reinterpret_cast<ShaderCreateInfo *>( + code_generator_output->create_info); + + shader_create_info.local_group_size(16, 16); + + /* The resources are added without explicit locations, so make sure it is done by the + * shader creator. */ + shader_create_info.auto_resource_location(true); + + /* Add implementation for implicit conversion operations inserted by the code generator. This + * file should include the functions [float|vec3|vec4]_from_[float|vec3|vec4]. */ + shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl"); + + /* The source shader is a compute shader with a main function that calls the dynamically + * generated evaluate function. The evaluate function includes the serialized GPU material graph + * preceded by code that initialized the inputs of the operation. Additionally, the storer + * functions that writes the outputs are defined outside the evaluate function. */ + shader_create_info.compute_source("gpu_shader_compositor_main.glsl"); + + /* The main function is emitted in the shader before the evaluate function, so the evaluate + * function needs to be forward declared here. */ + shader_create_info.typedef_source_generated += "void evaluate();\n"; + + operation->generate_code_for_outputs(shader_create_info); + + shader_create_info.compute_source_generated += "void evaluate()\n{\n"; + + operation->generate_code_for_inputs(material, shader_create_info); + + shader_create_info.compute_source_generated += code_generator_output->composite; + + shader_create_info.compute_source_generated += "}\n"; +} + +static eGPUTextureFormat texture_format_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return GPU_R16F; + case ResultType::Vector: + return GPU_RGBA16F; + case ResultType::Color: + return GPU_RGBA16F; + } + + BLI_assert_unreachable(); + return GPU_RGBA16F; +} + +/* Texture storers in the shader always take a vec4 as an argument, so encode each type in a vec4 + * appropriately. */ +static const char *glsl_store_expression_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "vec4(value)"; + case ResultType::Vector: + return "vec4(vector, 0.0)"; + case ResultType::Color: + return "color"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info) +{ + const std::string store_float_function_header = "void store_float(const uint id, float value)"; + const std::string store_vector_function_header = "void store_vector(const uint id, vec3 vector)"; + const std::string store_color_function_header = "void store_color(const uint id, vec4 color)"; + + /* The store functions are used by the node_compositor_store_output_[float|vector|color] + * functions but are only defined later as part of the compute source, so they need to be forward + * declared. */ + shader_create_info.typedef_source_generated += store_float_function_header + ";\n"; + shader_create_info.typedef_source_generated += store_vector_function_header + ";\n"; + shader_create_info.typedef_source_generated += store_color_function_header + ";\n"; + + /* Each of the store functions is essentially a single switch case on the given ID, so start by + * opening the function with a curly bracket followed by opening a switch statement in each of + * the functions. */ + std::stringstream store_float_function; + std::stringstream store_vector_function; + std::stringstream store_color_function; + const std::string store_function_start = "\n{\n switch (id) {\n"; + store_float_function << store_float_function_header << store_function_start; + store_vector_function << store_vector_function_header << store_function_start; + store_color_function << store_color_function_header << store_function_start; + + for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) { + const Result &result = get_result(output_identifier); + + /* Add a write-only image for this output where its values will be written. */ + shader_create_info.image(0, + texture_format_from_result_type(result.type()), + Qualifier::WRITE, + ImageType::FLOAT_2D, + output_identifier, + Frequency::BATCH); + + /* Add a case for the index of this output followed by a break statement. */ + std::stringstream case_code; + const std::string store_expression = glsl_store_expression_from_result_type(result.type()); + const std::string texel = ", ivec2(gl_GlobalInvocationID.xy), "; + case_code << " case " << StringRef(output_identifier).drop_known_prefix("output") << ":\n" + << " imageStore(" << output_identifier << texel << store_expression << ");\n" + << " break;\n"; + + /* Only add the case to the function with the matching type. */ + switch (result.type()) { + case ResultType::Float: + store_float_function << case_code.str(); + break; + case ResultType::Vector: + store_vector_function << case_code.str(); + break; + case ResultType::Color: + store_color_function << case_code.str(); + break; + } + } + + /* Close the previously opened switch statement as well as the function itself. */ + const std::string store_function_end = " }\n}\n\n"; + store_float_function << store_function_end; + store_vector_function << store_function_end; + store_color_function << store_function_end; + + shader_create_info.compute_source_generated += store_float_function.str() + + store_vector_function.str() + + store_color_function.str(); +} + +static const char *glsl_type_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "float"; + case ResultType::Vector: + return "vec3"; + case ResultType::Color: + return "vec4"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +/* Texture loaders in the shader always return a vec4, so a swizzle is needed to retrieve the + * actual value for each type. */ +static const char *glsl_swizzle_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "x"; + case ResultType::Vector: + return "xyz"; + case ResultType::Color: + return "rgba"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::generate_code_for_inputs(GPUMaterial *material, + ShaderCreateInfo &shader_create_info) +{ + /* The attributes of the GPU material represents the inputs of the operation. */ + ListBase attributes = GPU_material_attributes(material); + + /* Add a texture sampler for each of the inputs with the same name as the attribute. */ + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + shader_create_info.sampler(0, ImageType::FLOAT_2D, attribute->name, Frequency::BATCH); + } + + /* Declare a struct called var_attrs that includes an appropriately typed member for each of the + * inputs. The names of the members should be the letter v followed by the ID of the attribute + * corresponding to the input. Such names are expected by the code generator. */ + std::stringstream declare_attributes; + declare_attributes << "struct {\n"; + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name); + const std::string type = glsl_type_from_result_type(input_descriptor.type); + declare_attributes << " " << type << " v" << attribute->id << ";\n"; + } + declare_attributes << "} var_attrs;\n\n"; + + shader_create_info.compute_source_generated += declare_attributes.str(); + + /* The texture loader utilities are needed to sample the input textures and initialize the + * attributes. */ + shader_create_info.typedef_source("gpu_shader_compositor_texture_utilities.glsl"); + + /* Initialize each member of the previously declared struct by loading its corresponding texture + * with an appropriate swizzle for its type. */ + std::stringstream initialize_attributes; + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name); + const std::string swizzle = glsl_swizzle_from_result_type(input_descriptor.type); + initialize_attributes << "var_attrs.v" << attribute->id << " = " + << "texture_load(" << attribute->name + << ", ivec2(gl_GlobalInvocationID.xy))." << swizzle << ";\n"; + } + initialize_attributes << "\n"; + + shader_create_info.compute_source_generated += initialize_attributes.str(); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/simple_operation.cc b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc new file mode 100644 index 00000000000..d55a20e5c54 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "COM_input_descriptor.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +const StringRef SimpleOperation::input_identifier_ = StringRef("Input"); +const StringRef SimpleOperation::output_identifier_ = StringRef("Output"); + +Result &SimpleOperation::get_result() +{ + return Operation::get_result(output_identifier_); +} + +void SimpleOperation::map_input_to_result(Result *result) +{ + Operation::map_input_to_result(input_identifier_, result); +} + +void SimpleOperation::add_and_evaluate_input_processors() +{ +} + +Result &SimpleOperation::get_input() +{ + return Operation::get_input(input_identifier_); +} + +void SimpleOperation::switch_result_mapped_to_input(Result *result) +{ + Operation::switch_result_mapped_to_input(input_identifier_, result); +} + +void SimpleOperation::populate_result(Result result) +{ + Operation::populate_result(output_identifier_, result); + + /* The result of a simple operation is guaranteed to have a single user. */ + get_result().set_initial_reference_count(1); +} + +void SimpleOperation::declare_input_descriptor(InputDescriptor descriptor) +{ + Operation::declare_input_descriptor(input_identifier_, descriptor); +} + +InputDescriptor &SimpleOperation::get_input_descriptor() +{ + return Operation::get_input_descriptor(input_identifier_); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc new file mode 100644 index 00000000000..c9c8a056f87 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "GPU_shader.h" + +#include "COM_static_shader_manager.hh" + +namespace blender::realtime_compositor { + +StaticShaderManager::~StaticShaderManager() +{ + for (GPUShader *shader : shaders_.values()) { + GPU_shader_free(shader); + } +} + +GPUShader *StaticShaderManager::get(const char *info_name) +{ + /* If a shader with the same info name already exists in the manager, return it, otherwise, + * create a new shader from the info name and return it. */ + return shaders_.lookup_or_add_cb( + info_name, [info_name]() { return GPU_shader_create_from_info_name(info_name); }); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/texture_pool.cc b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc new file mode 100644 index 00000000000..1568970a030 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <cstdint> + +#include "BLI_hash.hh" +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_vector.hh" + +#include "GPU_texture.h" + +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Texture Pool Key. + */ + +TexturePoolKey::TexturePoolKey(int2 size, eGPUTextureFormat format) : size(size), format(format) +{ +} + +TexturePoolKey::TexturePoolKey(const GPUTexture *texture) +{ + size = int2(GPU_texture_width(texture), GPU_texture_height(texture)); + format = GPU_texture_format(texture); +} + +uint64_t TexturePoolKey::hash() const +{ + return get_default_hash_3(size.x, size.y, format); +} + +bool operator==(const TexturePoolKey &a, const TexturePoolKey &b) +{ + return a.size == b.size && a.format == b.format; +} + +/* -------------------------------------------------------------------- + * Texture Pool. + */ + +GPUTexture *TexturePool::acquire(int2 size, eGPUTextureFormat format) +{ + /* Check if there is an available texture with the required specification, and if one exists, + * return it. */ + const TexturePoolKey key = TexturePoolKey(size, format); + Vector<GPUTexture *> &available_textures = textures_.lookup_or_add_default(key); + if (!available_textures.is_empty()) { + return available_textures.pop_last(); + } + + /* Otherwise, allocate a new texture. */ + return allocate_texture(size, format); +} + +GPUTexture *TexturePool::acquire_color(int2 size) +{ + return acquire(size, GPU_RGBA16F); +} + +GPUTexture *TexturePool::acquire_vector(int2 size) +{ + /* Vectors are stored in RGBA textures because RGB textures have limited support. */ + return acquire(size, GPU_RGBA16F); +} + +GPUTexture *TexturePool::acquire_float(int2 size) +{ + return acquire(size, GPU_R16F); +} + +void TexturePool::release(GPUTexture *texture) +{ + textures_.lookup(TexturePoolKey(texture)).append(texture); +} + +void TexturePool::reset() +{ + textures_.clear(); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/utilities.cc b/source/blender/compositor/realtime_compositor/intern/utilities.cc new file mode 100644 index 00000000000..169ba70e9eb --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/utilities.cc @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_assert.h" +#include "BLI_function_ref.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" +#include "BLI_utildefines.h" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "GPU_compute.h" +#include "GPU_shader.h" + +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; +using TargetSocketPathInfo = DOutputSocket::TargetSocketPathInfo; + +DSocket get_input_origin_socket(DInputSocket input) +{ + /* The input is unlinked. Return the socket itself. */ + if (input->logically_linked_sockets().is_empty()) { + return input; + } + + /* Only a single origin socket is guaranteed to exist. */ + DSocket socket; + input.foreach_origin_socket([&](const DSocket origin) { socket = origin; }); + return socket; +} + +DOutputSocket get_output_linked_to_input(DInputSocket input) +{ + /* Get the origin socket of this input, which will be an output socket if the input is linked + * to an output. */ + const DSocket origin = get_input_origin_socket(input); + + /* If the origin socket is an input, that means the input is unlinked, so return a null output + * socket. */ + if (origin->is_input()) { + return DOutputSocket(); + } + + /* Now that we know the origin is an output, return a derived output from it. */ + return DOutputSocket(origin); +} + +ResultType get_node_socket_result_type(const SocketRef *socket) +{ + switch (socket->bsocket()->type) { + case SOCK_FLOAT: + return ResultType::Float; + case SOCK_VECTOR: + return ResultType::Vector; + case SOCK_RGBA: + return ResultType::Color; + default: + BLI_assert_unreachable(); + return ResultType::Float; + } +} + +bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef<bool(DNode)> condition) +{ + bool condition_satisfied = false; + output.foreach_target_socket( + [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) { + if (condition(target.node())) { + condition_satisfied = true; + return; + } + }); + return condition_satisfied; +} + +int number_of_inputs_linked_to_output_conditioned(DOutputSocket output, + FunctionRef<bool(DInputSocket)> condition) +{ + int count = 0; + output.foreach_target_socket( + [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) { + if (condition(target)) { + count++; + } + }); + return count; +} + +bool is_shader_node(DNode node) +{ + return node->typeinfo()->get_compositor_shader_node; +} + +bool is_node_supported(DNode node) +{ + return node->typeinfo()->get_compositor_operation || + node->typeinfo()->get_compositor_shader_node; +} + +InputDescriptor input_descriptor_from_input_socket(const InputSocketRef *socket) +{ + using namespace nodes; + InputDescriptor input_descriptor; + input_descriptor.type = get_node_socket_result_type(socket); + const NodeDeclaration *node_declaration = socket->node().declaration(); + /* Not every node have a declaration, in which case, we assume the default values for the rest of + * the properties. */ + if (!node_declaration) { + return input_descriptor; + } + const SocketDeclarationPtr &socket_declaration = node_declaration->inputs()[socket->index()]; + input_descriptor.domain_priority = socket_declaration->compositor_domain_priority(); + input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value(); + return input_descriptor; +} + +void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size) +{ + /* If the threads range is divisible by the local size, dispatch the number of needed groups, + * which is their division. If it is not divisible, then dispatch an extra group to cover the + * remaining invocations, which means the actual threads range of the dispatch will be a bit + * larger than the given one. */ + const int2 groups_to_dispatch = math::divide_ceil(threads_range, local_size); + GPU_compute_dispatch(shader, groups_to_dispatch.x, groups_to_dispatch.y, 1); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index 5353f71685c..097c377ece4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -13,6 +13,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_layer_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BLI_stack.h" @@ -95,6 +96,24 @@ bool DepsgraphBuilder::is_object_visibility_animated(const Object *object) return cache_->isPropertyAnimated(&object->id, property_id); } +bool DepsgraphBuilder::is_modifier_visibility_animated(const Object *object, + const ModifierData *modifier) +{ + AnimatedPropertyID property_id; + if (graph_->mode == DAG_EVAL_VIEWPORT) { + property_id = AnimatedPropertyID( + &object->id, &RNA_Modifier, (void *)modifier, "show_viewport"); + } + else if (graph_->mode == DAG_EVAL_RENDER) { + property_id = AnimatedPropertyID(&object->id, &RNA_Modifier, (void *)modifier, "show_render"); + } + else { + BLI_assert_msg(0, "Unknown evaluation mode."); + return false; + } + return cache_->isPropertyAnimated(&object->id, property_id); +} + bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan) { BLI_assert(object->type == OB_ARMATURE); diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index c44e5fd5f4d..5d043f1fd3a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -10,6 +10,7 @@ struct Base; struct ID; struct Main; +struct ModifierData; struct Object; struct bPoseChannel; @@ -25,6 +26,7 @@ class DepsgraphBuilder { virtual bool need_pull_base_into_graph(const Base *base); virtual bool is_object_visibility_animated(const Object *object); + virtual bool is_modifier_visibility_animated(const Object *object, const ModifierData *modifier); virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan); virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index be087c0b2d4..dd62a6cdea2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -769,11 +769,7 @@ void DepsgraphNodeBuilder::build_object(int base_index, build_object(-1, object->parent, DEG_ID_LINKED_INDIRECTLY, is_visible); } /* Modifiers. */ - if (object->modifiers.first != nullptr) { - BuilderWalkUserData data; - data.builder = this; - BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); - } + build_object_modifiers(object); /* Grease Pencil Modifiers. */ if (object->greasepencil_modifiers.first != nullptr) { BuilderWalkUserData data; @@ -877,6 +873,44 @@ void DepsgraphNodeBuilder::build_object_instance_collection(Object *object, bool is_parent_collection_visible_ = is_current_parent_collection_visible; } +void DepsgraphNodeBuilder::build_object_modifiers(Object *object) +{ + if (BLI_listbase_is_empty(&object->modifiers)) { + return; + } + + const ModifierMode modifier_mode = (graph_->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime : + eModifierMode_Render; + + IDNode *id_node = find_id_node(&object->id); + + add_operation_node(&object->id, + NodeType::GEOMETRY, + OperationCode::VISIBILITY, + [id_node](::Depsgraph *depsgraph) { + deg_evaluate_object_modifiers_mode_node_visibility(depsgraph, id_node); + }); + + LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) { + OperationNode *modifier_node = add_operation_node( + &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, nullptr, modifier->name); + + /* Mute modifier mode if the modifier is not enabled for the dependency graph mode. + * This handles static (non-animated) mode of the modifier. */ + if ((modifier->mode & modifier_mode) == 0) { + modifier_node->flag |= DEPSOP_FLAG_MUTE; + } + + if (is_modifier_visibility_animated(object, modifier)) { + graph_->has_animated_visibility = true; + } + } + + BuilderWalkUserData data; + data.builder = this; + BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); +} + void DepsgraphNodeBuilder::build_object_data(Object *object) { if (object->data == nullptr) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 18e28311132..d5ac601ebff 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -174,6 +174,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_object_flags(int base_index, Object *object, eDepsNode_LinkedState_Type linked_state); + virtual void build_object_modifiers(Object *object); virtual void build_object_data(Object *object); virtual void build_object_data_camera(Object *object); virtual void build_object_data_geometry(Object *object); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index fc5e5189e82..d6ee1286fc4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -298,7 +298,8 @@ void DepsgraphRelationBuilder::add_depends_on_transform_relation(const DepsNodeH { IDNode *id_node = handle->node->owner->owner; ID *id = id_node->id_orig; - ComponentKey geometry_key(id, NodeType::GEOMETRY); + const OperationKey geometry_key( + id, NodeType::GEOMETRY, OperationCode::MODIFIER, handle->node->name.c_str()); /* Wire up the actual relation. */ add_depends_on_transform_relation(id, geometry_key, description); } @@ -718,11 +719,7 @@ void DepsgraphRelationBuilder::build_object(Object *object) } /* Modifiers. */ - if (object->modifiers.first != nullptr) { - BuilderWalkUserData data; - data.builder = this; - BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); - } + build_object_modifiers(object); /* Grease Pencil Modifiers. */ if (object->greasepencil_modifiers.first != nullptr) { @@ -870,6 +867,63 @@ void DepsgraphRelationBuilder::build_object_layer_component_relations(Object *ob add_relation(object_from_layer_exit_key, synchronize_key, "Synchronize to Original"); } +void DepsgraphRelationBuilder::build_object_modifiers(Object *object) +{ + if (BLI_listbase_is_empty(&object->modifiers)) { + return; + } + + const OperationKey eval_init_key( + &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); + const OperationKey eval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + + const ComponentKey object_visibility_key(&object->id, NodeType::VISIBILITY); + const OperationKey modifier_visibility_key( + &object->id, NodeType::GEOMETRY, OperationCode::VISIBILITY); + add_relation(modifier_visibility_key, + object_visibility_key, + "modifier -> object visibility", + RELATION_NO_VISIBILITY_CHANGE); + + add_relation(modifier_visibility_key, eval_key, "modifier visibility -> geometry eval"); + + ModifierUpdateDepsgraphContext ctx = {}; + ctx.scene = scene_; + ctx.object = object; + + OperationKey previous_key = eval_init_key; + LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) { + const OperationKey modifier_key( + &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, modifier->name); + + /* Relation for the modifier stack chain. */ + add_relation(previous_key, modifier_key, "Modifier"); + + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)modifier->type); + if (mti->updateDepsgraph) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*modifier); + + DepsNodeHandle handle = create_node_handle(modifier_key); + ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); + mti->updateDepsgraph(modifier, &ctx); + } + + /* Time dependency. */ + if (BKE_modifier_depends_ontime(scene_, modifier)) { + const TimeSourceKey time_src_key; + add_relation(time_src_key, modifier_key, "Time Source -> Modifier"); + } + + previous_key = modifier_key; + } + add_relation(previous_key, eval_key, "modifier stack order"); + + /* Build IDs referenced by the modifiers. */ + BuilderWalkUserData data; + data.builder = this; + BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); +} + void DepsgraphRelationBuilder::build_object_data(Object *object) { if (object->data == nullptr) { @@ -2193,26 +2247,6 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) * evaluated prior to Scene's CoW is ready. */ OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL); add_relation(scene_key, obdata_ubereval_key, "CoW Relation", RELATION_FLAG_NO_FLUSH); - /* Modifiers */ - if (object->modifiers.first != nullptr) { - ModifierUpdateDepsgraphContext ctx = {}; - ctx.scene = scene_; - ctx.object = object; - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); - if (mti->updateDepsgraph) { - const BuilderStack::ScopedEntry stack_entry = stack_.trace(*md); - - DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); - ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); - mti->updateDepsgraph(md, &ctx); - } - if (BKE_modifier_depends_ontime(scene_, md)) { - TimeSourceKey time_src_key; - add_relation(time_src_key, obdata_ubereval_key, "Time Source -> Modifier"); - } - } - } /* Grease Pencil Modifiers. */ if (object->greasepencil_modifiers.first != nullptr) { ModifierUpdateDepsgraphContext ctx = {}; @@ -2256,8 +2290,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_LATTICE)) { // add geometry collider relations } - /* Make sure uber update is the last in the dependencies. */ - add_relation(geom_init_key, obdata_ubereval_key, "Object Geometry UberEval"); + /* Make sure uber update is the last in the dependencies. + * Only do it here unless there are modifiers. This avoids transitive relations. */ + if (BLI_listbase_is_empty(&object->modifiers)) { + OperationKey obdata_ubereval_key( + &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + add_relation(geom_init_key, obdata_ubereval_key, "Object Geometry UberEval"); + } if (object->type == OB_MBALL) { Object *mom = BKE_mball_basis_find(scene_, object); ComponentKey mom_geom_key(&mom->id, NodeType::GEOMETRY); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index db237303027..7d3a0fd9217 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -265,6 +265,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_object(Object *object); virtual void build_object_from_view_layer_base(Object *object); virtual void build_object_layer_component_relations(Object *object); + virtual void build_object_modifiers(Object *object); virtual void build_object_data(Object *object); virtual void build_object_data_camera(Object *object); virtual void build_object_data_geometry(Object *object); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 5202ada5408..d94746fb7fa 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -225,6 +225,10 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, } return node_identifier; } + + const char *prop_identifier = prop != nullptr ? RNA_property_identifier((PropertyRNA *)prop) : + ""; + if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) { const Object *object = reinterpret_cast<const Object *>(ptr->owner_id); const bConstraint *constraint = static_cast<const bConstraint *>(ptr->data); @@ -264,6 +268,13 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, return node_identifier; } } + else if (RNA_struct_is_a(ptr->type, &RNA_Modifier) && + (contains(prop_identifier, "show_viewport") || + contains(prop_identifier, "show_render"))) { + node_identifier.type = NodeType::GEOMETRY; + node_identifier.operation_code = OperationCode::VISIBILITY; + return node_identifier; + } else if (RNA_struct_is_a(ptr->type, &RNA_Mesh) || RNA_struct_is_a(ptr->type, &RNA_Modifier) || RNA_struct_is_a(ptr->type, &RNA_GpencilModifier) || RNA_struct_is_a(ptr->type, &RNA_Spline) || RNA_struct_is_a(ptr->type, &RNA_TextBox) || @@ -290,7 +301,6 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, else if (ptr->type == &RNA_Object) { /* Transforms props? */ if (prop != nullptr) { - const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop); /* TODO(sergey): How to optimize this? */ if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") || contains(prop_identifier, "matrix_channel") || diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h index 1bacb9abfa6..3f316fa84e8 100644 --- a/source/blender/depsgraph/intern/depsgraph_relation.h +++ b/source/blender/depsgraph/intern/depsgraph_relation.h @@ -28,6 +28,8 @@ enum RelationFlag { RELATION_FLAG_GODMODE = (1 << 4), /* Relation will check existence before being added. */ RELATION_CHECK_BEFORE_ADD = (1 << 5), + /* The relation does not participate in visibility checks. */ + RELATION_NO_VISIBILITY_CHANGE = (1 << 6), }; /* B depends on A (A -> B) */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 9b2ce2bb707..cd0015ff717 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -437,7 +437,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph) evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE); - if (graph->has_animated_visibility) { + if (graph->has_animated_visibility || graph->need_update_nodes_visibility) { /* Update pending parents including only the ones which are affecting operations which are * affecting visibility. */ state.need_update_pending_parents = true; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc index 05f7631b0d4..7b6aec0a73c 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc @@ -8,6 +8,7 @@ #include "intern/eval/deg_eval_visibility.h" #include "DNA_layer_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BLI_assert.h" @@ -47,6 +48,38 @@ void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node } } +void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node) +{ + BLI_assert(GS(id_node->id_cow->name) == ID_OB); + + Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph); + const Object *object = reinterpret_cast<const Object *>(id_node->id_cow); + + DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id); + + if (BLI_listbase_is_empty(&object->modifiers)) { + return; + } + + const ModifierMode modifier_mode = (graph->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime : + eModifierMode_Render; + + const ComponentNode *geometry_component = id_node->find_component(NodeType::GEOMETRY); + LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) { + OperationNode *modifier_node = geometry_component->find_operation(OperationCode::MODIFIER, + modifier->name); + + const bool modifier_enabled = modifier->mode & modifier_mode; + const int mute_flag = modifier_enabled ? 0 : DEPSOP_FLAG_MUTE; + if ((modifier_node->flag & DEPSOP_FLAG_MUTE) != mute_flag) { + modifier_node->flag &= ~DEPSOP_FLAG_MUTE; + modifier_node->flag |= mute_flag; + + graph->need_update_nodes_visibility = true; + } + } +} + void deg_graph_flush_visibility_flags(Depsgraph *graph) { enum { @@ -116,10 +149,30 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph) OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from); ComponentNode *comp_from = op_from->owner; + op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY); + + if (rel->flag & RELATION_NO_VISIBILITY_CHANGE) { + continue; + } + const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id; - const bool target_affects_visible_id = comp_to->affects_visible_id; - op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY); + bool target_affects_visible_id = comp_to->affects_visible_id; + + /* This is a bit arbitrary but the idea here is following: + * + * - When another object is used by a disabled modifier we do not want that object to + * be considered needed for evaluation. + * + * - However, we do not want to take mute flag during visibility propagation within the + * same object. Otherwise drivers and transform dependencies of the geometry component + * entry component might not be properly handled. + * + * This code works fine for muting modifiers, but might need tweaks when mute is used for + * something else. */ + if (comp_from != comp_to && (op_to->flag & DEPSOP_FLAG_MUTE)) { + target_affects_visible_id = false; + } /* Visibility component forces all components of the current ID to be considered as * affecting directly visible. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h index 9e9db8ab34a..6544654f3cf 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h @@ -18,6 +18,9 @@ struct IDNode; * restriction flags. */ void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node); +/* Update node visibility flags based on actual modifiers mode flags. */ +void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node); + /* Flush both static and dynamic visibility flags from leaves up to the roots, making it possible * to know whether a node has affect on something (potentially) visible. */ void deg_graph_flush_visibility_flags(Depsgraph *graph); diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index de4bc0668cf..016af735fcf 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -84,6 +84,8 @@ const char *operationCodeAsString(OperationCode opcode) /* Geometry. */ case OperationCode::GEOMETRY_EVAL_INIT: return "GEOMETRY_EVAL_INIT"; + case OperationCode::MODIFIER: + return "MODIFIER"; case OperationCode::GEOMETRY_EVAL: return "GEOMETRY_EVAL"; case OperationCode::GEOMETRY_EVAL_DONE: @@ -225,6 +227,16 @@ void OperationNode::tag_update(Depsgraph *graph, eUpdateSource source) * the evaluated clues that evaluation needs to happen again. */ graph->add_entry_tag(this); + /* Enforce dynamic visibility code-path update. + * This ensures visibility flags are consistently propagated throughout the dependency graph when + * there is no animated visibility in the graph. + * + * For example this ensures that graph is updated properly when manually toggling non-animated + * modifier visibility. */ + if (opcode == OperationCode::VISIBILITY) { + graph->need_update_nodes_visibility = true; + } + /* Tag for update, but also note that this was the source of an update. */ flag |= (DEPSOP_FLAG_NEEDS_UPDATE | DEPSOP_FLAG_DIRECTLY_MODIFIED); switch (source) { diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index 656b27550f6..cb3beb56556 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -84,6 +84,8 @@ enum class OperationCode { /* Initialize evaluation of the geometry. Is an entry operation of geometry * component. */ GEOMETRY_EVAL_INIT, + /* Modifier. */ + MODIFIER, /* Evaluate the whole geometry, including modifiers. */ GEOMETRY_EVAL, /* Evaluation of geometry is completely done. */ @@ -217,6 +219,9 @@ enum OperationFlag { /* The operation directly or indirectly affects ID node visibility. */ DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4), + /* Evaluation of the node is temporarily disabled. */ + DEPSOP_FLAG_MUTE = (1 << 5), + /* Set of flags which gets flushed along the relations. */ DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED), }; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index c34a6daa126..391c3bb3512 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ../nodes ../render ../render/intern + ../compositor/realtime_compositor ../windowmanager ../../../intern/atomic @@ -77,13 +78,13 @@ set(SRC intern/draw_cache_impl_mesh.cc intern/draw_cache_impl_metaball.c intern/draw_cache_impl_particles.c - intern/draw_cache_impl_pointcloud.c + intern/draw_cache_impl_pointcloud.cc intern/draw_cache_impl_subdivision.cc intern/draw_cache_impl_volume.c intern/draw_color_management.cc intern/draw_common.c intern/draw_curves.cc - intern/draw_debug.c + intern/draw_debug.cc intern/draw_fluid.c intern/draw_hair.cc intern/draw_instance_data.c @@ -103,6 +104,7 @@ set(SRC intern/smaa_textures.c engines/basic/basic_engine.c engines/basic/basic_shader.c + engines/compositor/compositor_engine.cc engines/image/image_engine.cc engines/image/image_shader.cc engines/eevee/eevee_bloom.c @@ -214,6 +216,7 @@ set(SRC intern/draw_common_shader_shared.h intern/draw_curves_private.h intern/draw_debug.h + intern/draw_debug.hh intern/draw_hair_private.h intern/draw_instance_data.h intern/draw_manager.h @@ -230,6 +233,7 @@ set(SRC intern/smaa_textures.h engines/basic/basic_engine.h engines/basic/basic_private.h + engines/compositor/compositor_engine.h engines/eevee/eevee_engine.h engines/eevee/eevee_lightcache.h engines/eevee/eevee_lut.h @@ -261,6 +265,7 @@ set(SRC set(LIB bf_blenkernel bf_blenlib + bf_realtime_compositor bf_windowmanager ) @@ -434,20 +439,19 @@ set(GLSL_SRC intern/shaders/common_attribute_lib.glsl intern/shaders/common_colormanagement_lib.glsl + intern/shaders/common_debug_draw_lib.glsl + intern/shaders/common_debug_print_lib.glsl + intern/shaders/common_fullscreen_vert.glsl + intern/shaders/common_fxaa_lib.glsl intern/shaders/common_globals_lib.glsl intern/shaders/common_gpencil_lib.glsl - intern/shaders/common_pointcloud_lib.glsl intern/shaders/common_hair_lib.glsl - intern/shaders/common_hair_refine_vert.glsl intern/shaders/common_hair_refine_comp.glsl - intern/shaders/common_math_lib.glsl + intern/shaders/common_hair_refine_vert.glsl intern/shaders/common_math_geom_lib.glsl - intern/shaders/common_view_clipping_lib.glsl - intern/shaders/common_view_lib.glsl - intern/shaders/common_fxaa_lib.glsl + intern/shaders/common_math_lib.glsl + intern/shaders/common_pointcloud_lib.glsl intern/shaders/common_smaa_lib.glsl - intern/shaders/common_fullscreen_vert.glsl - intern/shaders/common_subdiv_custom_data_interp_comp.glsl intern/shaders/common_subdiv_ibo_lines_comp.glsl intern/shaders/common_subdiv_ibo_tris_comp.glsl @@ -460,6 +464,13 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl intern/shaders/common_subdiv_vbo_lnor_comp.glsl intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl + intern/shaders/common_view_clipping_lib.glsl + intern/shaders/common_view_lib.glsl + intern/shaders/draw_debug_draw_display_frag.glsl + intern/shaders/draw_debug_draw_display_vert.glsl + intern/shaders/draw_debug_info.hh + intern/shaders/draw_debug_print_display_frag.glsl + intern/shaders/draw_debug_print_display_vert.glsl intern/draw_common_shader_shared.h intern/draw_shader_shared.h diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc new file mode 100644 index 00000000000..f36a59a4ce6 --- /dev/null +++ b/source/blender/draw/engines/compositor/compositor_engine.cc @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_listbase.h" +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "DNA_ID_enums.h" +#include "DNA_scene_types.h" + +#include "DEG_depsgraph_query.h" + +#include "DRW_render.h" + +#include "IMB_colormanagement.h" + +#include "COM_context.hh" +#include "COM_evaluator.hh" +#include "COM_texture_pool.hh" + +#include "GPU_texture.h" + +namespace blender::draw::compositor { + +class TexturePool : public realtime_compositor::TexturePool { + public: + GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) override + { + DrawEngineType *owner = (DrawEngineType *)this; + return DRW_texture_pool_query_2d(size.x, size.y, format, owner); + } +}; + +class Context : public realtime_compositor::Context { + private: + /* A pointer to the info message of the compositor engine. This is a char array of size + * GPU_INFO_SIZE. The message is cleared prior to updating or evaluating the compositor. */ + char *info_message_; + + public: + Context(realtime_compositor::TexturePool &texture_pool, char *info_message) + : realtime_compositor::Context(texture_pool), info_message_(info_message) + { + } + + const Scene *get_scene() const override + { + return DRW_context_state_get()->scene; + } + + int2 get_output_size() override + { + return int2(float2(DRW_viewport_size_get())); + } + + GPUTexture *get_output_texture() override + { + return DRW_viewport_texture_list_get()->color; + } + + GPUTexture *get_input_texture(int UNUSED(view_layer), eScenePassType UNUSED(pass_type)) override + { + return get_output_texture(); + } + + StringRef get_view_name() override + { + const SceneRenderView *view = static_cast<SceneRenderView *>( + BLI_findlink(&get_scene()->r.views, DRW_context_state_get()->v3d->multiview_eye)); + return view->name; + } + + void set_info_message(StringRef message) const override + { + message.copy(info_message_, GPU_INFO_SIZE); + } +}; + +class Engine { + private: + TexturePool texture_pool_; + Context context_; + realtime_compositor::Evaluator evaluator_; + /* Stores the viewport size at the time the last compositor evaluation happened. See the + * update_viewport_size method for more information. */ + int2 last_viewport_size_; + + public: + Engine(char *info_message) + : context_(texture_pool_, info_message), + evaluator_(context_, node_tree()), + last_viewport_size_(context_.get_output_size()) + { + } + + /* Update the viewport size and evaluate the compositor. */ + void draw() + { + update_viewport_size(); + evaluator_.evaluate(); + } + + /* If the size of the viewport changed from the last time the compositor was evaluated, update + * the viewport size and reset the evaluator. That's because the evaluator compiles the node tree + * in a manner that is specifically optimized for the size of the viewport. This should be called + * before evaluating the compositor. */ + void update_viewport_size() + { + if (last_viewport_size_ == context_.get_output_size()) { + return; + } + + last_viewport_size_ = context_.get_output_size(); + + evaluator_.reset(); + } + + /* If the compositor node tree changed, reset the evaluator. */ + void update(const Depsgraph *depsgraph) + { + if (DEG_id_type_updated(depsgraph, ID_NT)) { + evaluator_.reset(); + } + } + + /* Get a reference to the compositor node tree. */ + static bNodeTree &node_tree() + { + return *DRW_context_state_get()->scene->nodetree; + } +}; + +} // namespace blender::draw::compositor + +using namespace blender::draw::compositor; + +struct COMPOSITOR_Data { + DrawEngineType *engine_type; + DRWViewportEmptyList *fbl; + DRWViewportEmptyList *txl; + DRWViewportEmptyList *psl; + DRWViewportEmptyList *stl; + Engine *instance_data; + char info[GPU_INFO_SIZE]; +}; + +static void compositor_engine_init(void *data) +{ + COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data); + + if (!compositor_data->instance_data) { + compositor_data->instance_data = new Engine(compositor_data->info); + } +} + +static void compositor_engine_free(void *instance_data) +{ + Engine *engine = static_cast<Engine *>(instance_data); + delete engine; +} + +static void compositor_engine_draw(void *data) +{ + const COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data); + compositor_data->instance_data->draw(); +} + +static void compositor_engine_update(void *data) +{ + COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data); + + /* Clear any info message that was set in a previous update. */ + compositor_data->info[0] = '\0'; + + if (compositor_data->instance_data) { + compositor_data->instance_data->update(DRW_context_state_get()->depsgraph); + } +} + +extern "C" { + +static const DrawEngineDataSize compositor_data_size = DRW_VIEWPORT_DATA_SIZE(COMPOSITOR_Data); + +DrawEngineType draw_engine_compositor_type = { + nullptr, /* next */ + nullptr, /* prev */ + N_("Compositor"), /* idname */ + &compositor_data_size, /* vedata_size */ + &compositor_engine_init, /* engine_init */ + nullptr, /* engine_free */ + &compositor_engine_free, /* instance_free */ + nullptr, /* cache_init */ + nullptr, /* cache_populate */ + nullptr, /* cache_finish */ + &compositor_engine_draw, /* draw_scene */ + &compositor_engine_update, /* view_update */ + nullptr, /* id_update */ + nullptr, /* render_to_image */ + nullptr, /* store_metadata */ +}; +} diff --git a/source/blender/draw/engines/compositor/compositor_engine.h b/source/blender/draw/engines/compositor/compositor_engine.h new file mode 100644 index 00000000000..5de0de8a0b3 --- /dev/null +++ b/source/blender/draw/engines/compositor/compositor_engine.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +extern DrawEngineType draw_engine_compositor_type; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c index 536242f67d8..a3ab4cdb830 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c +++ b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c @@ -357,7 +357,7 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat); #ifdef DEBUG_CSM - DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true); + DRW_debug_m4_as_bbox(viewprojmat, true, dbg_col); #endif } diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh index 94ff694b147..b398a6cc4e7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "eevee_defines.hh" #include "gpu_shader_create_info.hh" diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh index e32020f2be6..d6ff34b0ed2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "eevee_defines.hh" #include "gpu_shader_create_info.hh" diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index e38695c76ab..df5ee6a18c0 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -2220,7 +2220,7 @@ static void draw_armature_edit(ArmatureDrawContext *ctx) const bool show_text = DRW_state_show_text(); const Object *ob_orig = DEG_get_original_object(ob); - /* FIXME(campbell): We should be able to use the CoW object, + /* FIXME(@campbellbarton): We should be able to use the CoW object, * however the active bone isn't updated. Long term solution is an 'EditArmature' struct. * for now we can draw from the original armature. See: T66773. */ // bArmature *arm = ob->data; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index af8e58c78f8..0159c9fc86e 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -228,9 +228,9 @@ static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCa } } else { - const MPoly *mp = &mr->mpoly[0]; - for (int i = 0; i < mr->poly_len; i++, mp++) { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + for (int i = 0; i < mr->poly_len; i++) { + if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[i])) { + const MPoly *mp = &mr->mpoly[i]; const int mat = min_ii(mp->mat_nr, mat_last); tri_first_index[i] = mat_tri_offs[mat]; mat_tri_offs[mat] += mp->totloop - 2; @@ -269,7 +269,7 @@ static void mesh_render_data_mat_tri_len_mesh_range_fn(void *__restrict userdata int *mat_tri_len = static_cast<int *>(tls->userdata_chunk); const MPoly *mp = &mr->mpoly[iter]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[iter])) { int mat = min_ii(mp->mat_nr, mr->mat_len - 1); mat_tri_len[mat] += mp->totloop - 2; } @@ -332,7 +332,7 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, if (mr->extract_type != MR_EXTRACT_BMESH) { /* Mesh */ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - /* NOTE(campbell): It's possible to skip allocating tessellation, + /* NOTE(@campbellbarton): It's possible to skip allocating tessellation, * the tessellation can be calculated as part of the iterator, see: P2188. * The overall advantage is small (around 1%), so keep this as-is. */ mr->mlooptri = static_cast<MLoopTri *>( @@ -578,6 +578,13 @@ MeshRenderData *mesh_render_data_create(Object *object, mr->v_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX)); mr->e_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX)); mr->p_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX)); + + mr->hide_vert = static_cast<const bool *>( + CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert")); + mr->hide_edge = static_cast<const bool *>( + CustomData_get_layer_named(&me->edata, CD_PROP_BOOL, ".hide_edge")); + mr->hide_poly = static_cast<const bool *>( + CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly")); } else { /* #BMesh */ diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc index 55d0eee00e5..d99af0c77e4 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc @@ -13,24 +13,23 @@ #include "BLI_math_base.h" #include "BLI_math_vector.h" +#include "BLI_task.hh" #include "BLI_utildefines.h" #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" -#include "BKE_customdata.h" +#include "BKE_attribute.hh" #include "BKE_pointcloud.h" #include "GPU_batch.h" #include "draw_cache_impl.h" /* own include */ -static void pointcloud_batch_cache_clear(PointCloud *pointcloud); - /* ---------------------------------------------------------------------- */ /* PointCloud GPUBatch Cache */ -typedef struct PointCloudBatchCache { +struct PointCloudBatchCache { GPUVertBuf *pos; /* Position and radius. */ GPUVertBuf *geom; /* Instanced geometry for each point in the cloud (small sphere). */ GPUIndexBuf *geom_indices; @@ -43,57 +42,50 @@ typedef struct PointCloudBatchCache { bool is_dirty; int mat_len; -} PointCloudBatchCache; +}; /* GPUBatch cache management. */ -static bool pointcloud_batch_cache_valid(PointCloud *pointcloud) +static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud &pointcloud) +{ + return static_cast<PointCloudBatchCache *>(pointcloud.batch_cache); +} + +static bool pointcloud_batch_cache_valid(PointCloud &pointcloud) { - PointCloudBatchCache *cache = pointcloud->batch_cache; + PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (cache == NULL) { return false; } - if (cache->mat_len != DRW_pointcloud_material_count_get(pointcloud)) { + if (cache->mat_len != DRW_pointcloud_material_count_get(&pointcloud)) { return false; } return cache->is_dirty == false; } -static void pointcloud_batch_cache_init(PointCloud *pointcloud) +static void pointcloud_batch_cache_init(PointCloud &pointcloud) { - PointCloudBatchCache *cache = pointcloud->batch_cache; + PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (!cache) { - cache = pointcloud->batch_cache = MEM_callocN(sizeof(*cache), __func__); + cache = MEM_cnew<PointCloudBatchCache>(__func__); + pointcloud.batch_cache = cache; } else { memset(cache, 0, sizeof(*cache)); } - cache->mat_len = DRW_pointcloud_material_count_get(pointcloud); - cache->surface_per_mat = MEM_callocN(sizeof(GPUBatch *) * cache->mat_len, - "pointcloud suface_per_mat"); + cache->mat_len = DRW_pointcloud_material_count_get(&pointcloud); + cache->surface_per_mat = static_cast<GPUBatch **>( + MEM_callocN(sizeof(GPUBatch *) * cache->mat_len, __func__)); cache->is_dirty = false; } -void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud) -{ - if (!pointcloud_batch_cache_valid(pointcloud)) { - pointcloud_batch_cache_clear(pointcloud); - pointcloud_batch_cache_init(pointcloud); - } -} - -static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud *pointcloud) -{ - return pointcloud->batch_cache; -} - void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode) { - PointCloudBatchCache *cache = pointcloud->batch_cache; + PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud); if (cache == NULL) { return; } @@ -106,9 +98,9 @@ void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode) } } -static void pointcloud_batch_cache_clear(PointCloud *pointcloud) +static void pointcloud_batch_cache_clear(PointCloud &pointcloud) { - PointCloudBatchCache *cache = pointcloud->batch_cache; + PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (!cache) { return; } @@ -127,54 +119,65 @@ static void pointcloud_batch_cache_clear(PointCloud *pointcloud) MEM_SAFE_FREE(cache->surface_per_mat); } +void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud) +{ + if (!pointcloud_batch_cache_valid(*pointcloud)) { + pointcloud_batch_cache_clear(*pointcloud); + pointcloud_batch_cache_init(*pointcloud); + } +} + void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud) { - pointcloud_batch_cache_clear(pointcloud); + pointcloud_batch_cache_clear(*pointcloud); MEM_SAFE_FREE(pointcloud->batch_cache); } -static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache *cache) +static void pointcloud_batch_cache_ensure_pos(const PointCloud &pointcloud, + PointCloudBatchCache &cache) { - if (cache->pos != NULL) { + using namespace blender; + if (cache.pos != NULL) { return; } - PointCloud *pointcloud = ob->data; - const float(*positions)[3] = (float(*)[3])CustomData_get_layer_named( - &pointcloud->pdata, CD_PROP_FLOAT3, "position"); - const float *radii = (float *)CustomData_get_layer_named( - &pointcloud->pdata, CD_PROP_FLOAT, "radius"); - const bool has_radius = radii != NULL; - - static GPUVertFormat format = {0}; - static GPUVertFormat format_no_radius = {0}; - static uint pos; - if (format.attr_len == 0) { - /* initialize vertex format */ - /* From the opengl wiki: - * Note that size does not have to exactly match the size used by the vertex shader. If the - * vertex shader has fewer components than the attribute provides, then the extras are ignored. - * If the vertex shader has more components than the array provides, the extras are given - * values from the vector (0, 0, 0, 1) for the missing XYZW components. - */ - pos = GPU_vertformat_attr_add(&format_no_radius, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - cache->pos = GPU_vertbuf_create_with_format(has_radius ? &format : &format_no_radius); - GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint); - - if (has_radius) { - float(*vbo_data)[4] = (float(*)[4])GPU_vertbuf_get_data(cache->pos); - for (int i = 0; i < pointcloud->totpoint; i++) { - copy_v3_v3(vbo_data[i], positions[i]); - /* TODO(fclem): remove multiplication here. - * Here only for keeping the size correct for now. */ - vbo_data[i][3] = radii[i] * 100.0f; + const bke::AttributeAccessor attributes = bke::pointcloud_attributes(pointcloud); + const VArraySpan<float3> positions = attributes.lookup<float3>("position", ATTR_DOMAIN_POINT); + const VArray<float> radii = attributes.lookup<float>("radius", ATTR_DOMAIN_POINT); + /* From the opengl wiki: + * Note that size does not have to exactly match the size used by the vertex shader. If the + * vertex shader has fewer components than the attribute provides, then the extras are ignored. + * If the vertex shader has more components than the array provides, the extras are given + * values from the vector (0, 0, 0, 1) for the missing XYZW components. */ + if (radii) { + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } + cache.pos = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache.pos, positions.size()); + const VArraySpan<float> radii_span(radii); + MutableSpan<float4> vbo_data{static_cast<float4 *>(GPU_vertbuf_get_data(cache.pos)), + pointcloud.totpoint}; + threading::parallel_for(vbo_data.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + vbo_data[i].x = positions[i].x; + vbo_data[i].y = positions[i].y; + vbo_data[i].z = positions[i].z; + /* TODO(fclem): remove multiplication. Here only for keeping the size correct for now. */ + vbo_data[i].w = radii_span[i] * 100.0f; + } + }); } else { - GPU_vertbuf_attr_fill(cache->pos, pos, positions); + static GPUVertFormat format = {0}; + static uint pos; + if (format.attr_len == 0) { + pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + cache.pos = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache.pos, positions.size()); + GPU_vertbuf_attr_fill(cache.pos, pos, positions.data()); } } @@ -193,24 +196,23 @@ static const uint half_octahedron_tris[4][3] = { {0, 4, 1}, }; -static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBatchCache *cache) +static void pointcloud_batch_cache_ensure_geom(PointCloudBatchCache &cache) { - if (cache->geom != NULL) { + if (cache.geom != NULL) { return; } static GPUVertFormat format = {0}; static uint pos; if (format.attr_len == 0) { - /* initialize vertex format */ pos = GPU_vertformat_attr_add(&format, "pos_inst", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "nor"); } - cache->geom = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(cache->geom, ARRAY_SIZE(half_octahedron_normals)); + cache.geom = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache.geom, ARRAY_SIZE(half_octahedron_normals)); - GPU_vertbuf_attr_fill(cache->geom, pos, half_octahedron_normals); + GPU_vertbuf_attr_fill(cache.geom, pos, half_octahedron_normals); GPUIndexBufBuilder builder; GPU_indexbuf_init(&builder, @@ -222,16 +224,16 @@ static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBat GPU_indexbuf_add_tri_verts(&builder, UNPACK3(half_octahedron_tris[i])); } - cache->geom_indices = GPU_indexbuf_build(&builder); + cache.geom_indices = GPU_indexbuf_build(&builder); } GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob) { - PointCloud *pointcloud = ob->data; + PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data); PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (cache->dots == NULL) { - pointcloud_batch_cache_ensure_pos(ob, cache); + pointcloud_batch_cache_ensure_pos(pointcloud, *cache); cache->dots = GPU_batch_create(GPU_PRIM_POINTS, cache->pos, NULL); } @@ -240,12 +242,12 @@ GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob) GPUBatch *DRW_pointcloud_batch_cache_get_surface(Object *ob) { - PointCloud *pointcloud = ob->data; + PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data); PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (cache->surface == NULL) { - pointcloud_batch_cache_ensure_pos(ob, cache); - pointcloud_batch_cache_ensure_geom(ob, cache); + pointcloud_batch_cache_ensure_pos(pointcloud, *cache); + pointcloud_batch_cache_ensure_geom(*cache); cache->surface = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices); GPU_batch_instbuf_add_ex(cache->surface, cache->pos, false); @@ -258,14 +260,14 @@ GPUBatch **DRW_cache_pointcloud_surface_shaded_get(Object *ob, struct GPUMaterial **UNUSED(gpumat_array), uint gpumat_array_len) { - PointCloud *pointcloud = ob->data; + PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data); PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); BLI_assert(cache->mat_len == gpumat_array_len); UNUSED_VARS(gpumat_array_len); if (cache->surface_per_mat[0] == NULL) { - pointcloud_batch_cache_ensure_pos(ob, cache); - pointcloud_batch_cache_ensure_geom(ob, cache); + pointcloud_batch_cache_ensure_pos(pointcloud, *cache); + pointcloud_batch_cache_ensure_geom(*cache); cache->surface_per_mat[0] = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices); GPU_batch_instbuf_add_ex(cache->surface_per_mat[0], cache->pos, false); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 2f8a2540776..075e08eb49f 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -668,7 +668,9 @@ static void draw_subdiv_cache_extra_coarse_face_data_bm(BMesh *bm, } } -static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t *flags_data) +static void draw_subdiv_cache_extra_coarse_face_data_mesh(const MeshRenderData *mr, + Mesh *mesh, + uint32_t *flags_data) { for (int i = 0; i < mesh->totpoly; i++) { uint32_t flag = 0; @@ -678,7 +680,7 @@ static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t * if ((mesh->mpoly[i].flag & ME_FACE_SEL) != 0) { flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; } - if ((mesh->mpoly[i].flag & ME_HIDE) != 0) { + if (mr->hide_poly && mr->hide_poly[i]) { flag |= SUBDIV_COARSE_FACE_FLAG_HIDDEN; } flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); @@ -691,7 +693,7 @@ static void draw_subdiv_cache_extra_coarse_face_data_mapped(Mesh *mesh, uint32_t *flags_data) { if (bm == nullptr) { - draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data); + draw_subdiv_cache_extra_coarse_face_data_mesh(mr, mesh, flags_data); return; } @@ -726,7 +728,7 @@ static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cach draw_subdiv_cache_extra_coarse_face_data_mapped(mesh, cache->bm, mr, flags_data); } else { - draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data); + draw_subdiv_cache_extra_coarse_face_data_mesh(mr, mesh, flags_data); } /* Make sure updated data is re-uploaded. */ diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c deleted file mode 100644 index b568119627e..00000000000 --- a/source/blender/draw/intern/draw_debug.c +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2018 Blender Foundation. */ - -/** \file - * \ingroup draw - * - * \brief Simple API to draw debug shapes in the viewport. - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_object_types.h" - -#include "BKE_object.h" - -#include "BLI_link_utils.h" - -#include "GPU_immediate.h" -#include "GPU_matrix.h" - -#include "draw_debug.h" -#include "draw_manager.h" - -/* --------- Register --------- */ - -/* Matrix applied to all points before drawing. Could be a stack if needed. */ -static float g_modelmat[4][4]; - -void DRW_debug_modelmat_reset(void) -{ - unit_m4(g_modelmat); -} - -void DRW_debug_modelmat(const float modelmat[4][4]) -{ - copy_m4_m4(g_modelmat, modelmat); -} - -void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]) -{ - DRWDebugLine *line = MEM_mallocN(sizeof(DRWDebugLine), "DRWDebugLine"); - mul_v3_m4v3(line->pos[0], g_modelmat, v1); - mul_v3_m4v3(line->pos[1], g_modelmat, v2); - copy_v4_v4(line->color, color); - BLI_LINKS_PREPEND(DST.debug.lines, line); -} - -void DRW_debug_polygon_v3(const float (*v)[3], const int vert_len, const float color[4]) -{ - BLI_assert(vert_len > 1); - - for (int i = 0; i < vert_len; i++) { - DRW_debug_line_v3v3(v[i], v[(i + 1) % vert_len], color); - } -} - -void DRW_debug_m4(const float m[4][4]) -{ - float v0[3] = {0.0f, 0.0f, 0.0f}; - float v1[3] = {1.0f, 0.0f, 0.0f}; - float v2[3] = {0.0f, 1.0f, 0.0f}; - float v3[3] = {0.0f, 0.0f, 1.0f}; - - mul_m4_v3(m, v0); - mul_m4_v3(m, v1); - mul_m4_v3(m, v2); - mul_m4_v3(m, v3); - - DRW_debug_line_v3v3(v0, v1, (float[4]){1.0f, 0.0f, 0.0f, 1.0f}); - DRW_debug_line_v3v3(v0, v2, (float[4]){0.0f, 1.0f, 0.0f, 1.0f}); - DRW_debug_line_v3v3(v0, v3, (float[4]){0.0f, 0.0f, 1.0f, 1.0f}); -} - -void DRW_debug_bbox(const BoundBox *bbox, const float color[4]) -{ - DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[1], color); - DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[2], color); - DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[3], color); - DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[0], color); - - DRW_debug_line_v3v3(bbox->vec[4], bbox->vec[5], color); - DRW_debug_line_v3v3(bbox->vec[5], bbox->vec[6], color); - DRW_debug_line_v3v3(bbox->vec[6], bbox->vec[7], color); - DRW_debug_line_v3v3(bbox->vec[7], bbox->vec[4], color); - - DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[4], color); - DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[5], color); - DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[6], color); - DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[7], color); -} - -void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], const bool invert) -{ - BoundBox bb; - const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; - float project_matrix[4][4]; - if (invert) { - invert_m4_m4(project_matrix, m); - } - else { - copy_m4_m4(project_matrix, m); - } - - BKE_boundbox_init_from_minmax(&bb, min, max); - for (int i = 0; i < 8; i++) { - mul_project_m4_v3(project_matrix, bb.vec[i]); - } - DRW_debug_bbox(&bb, color); -} - -void DRW_debug_sphere(const float center[3], const float radius, const float color[4]) -{ - float size_mat[4][4]; - DRWDebugSphere *sphere = MEM_mallocN(sizeof(DRWDebugSphere), "DRWDebugSphere"); - /* Bake all transform into a Matrix4 */ - scale_m4_fl(size_mat, radius); - copy_m4_m4(sphere->mat, g_modelmat); - translate_m4(sphere->mat, center[0], center[1], center[2]); - mul_m4_m4m4(sphere->mat, sphere->mat, size_mat); - - copy_v4_v4(sphere->color, color); - BLI_LINKS_PREPEND(DST.debug.spheres, sphere); -} - -/* --------- Render --------- */ - -static void drw_debug_draw_lines(void) -{ - int count = BLI_linklist_count((LinkNode *)DST.debug.lines); - - if (count == 0) { - return; - } - - GPUVertFormat *vert_format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(vert_format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR); - - immBegin(GPU_PRIM_LINES, count * 2); - - while (DST.debug.lines) { - void *next = DST.debug.lines->next; - - immAttr4fv(col, DST.debug.lines->color); - immVertex3fv(pos, DST.debug.lines->pos[0]); - - immAttr4fv(col, DST.debug.lines->color); - immVertex3fv(pos, DST.debug.lines->pos[1]); - - MEM_freeN(DST.debug.lines); - DST.debug.lines = next; - } - immEnd(); - - immUnbindProgram(); -} - -static void drw_debug_draw_spheres(void) -{ - int count = BLI_linklist_count((LinkNode *)DST.debug.spheres); - - if (count == 0) { - return; - } - - float persmat[4][4]; - DRW_view_persmat_get(NULL, persmat, false); - - GPUBatch *empty_sphere = DRW_cache_empty_sphere_get(); - GPU_batch_program_set_builtin(empty_sphere, GPU_SHADER_3D_UNIFORM_COLOR); - while (DST.debug.spheres) { - void *next = DST.debug.spheres->next; - float MVP[4][4]; - - mul_m4_m4m4(MVP, persmat, DST.debug.spheres->mat); - GPU_batch_uniform_mat4(empty_sphere, "ModelViewProjectionMatrix", MVP); - GPU_batch_uniform_4fv(empty_sphere, "color", DST.debug.spheres->color); - GPU_batch_draw(empty_sphere); - - MEM_freeN(DST.debug.spheres); - DST.debug.spheres = next; - } -} - -void drw_debug_draw(void) -{ - drw_debug_draw_lines(); - drw_debug_draw_spheres(); -} - -void drw_debug_init(void) -{ - DRW_debug_modelmat_reset(); -} diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc new file mode 100644 index 00000000000..aaf18014143 --- /dev/null +++ b/source/blender/draw/intern/draw_debug.cc @@ -0,0 +1,721 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2018 Blender Foundation. */ + +/** \file + * \ingroup draw + * + * \brief Simple API to draw debug shapes in the viewport. + */ + +#include "BKE_object.h" +#include "BLI_link_utils.h" +#include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_debug.h" + +#include "draw_debug.h" +#include "draw_debug.hh" +#include "draw_manager.h" +#include "draw_shader.h" +#include "draw_shader_shared.h" + +#include <iomanip> + +namespace blender::draw { + +/* -------------------------------------------------------------------- */ +/** \name Init and state + * \{ */ + +DebugDraw::DebugDraw() +{ + constexpr int circle_resolution = 16; + for (auto axis : IndexRange(3)) { + for (auto edge : IndexRange(circle_resolution)) { + for (auto vert : IndexRange(2)) { + const float angle = (2 * M_PI) * (edge + vert) / float(circle_resolution); + float point[3] = {cosf(angle), sinf(angle), 0.0f}; + sphere_verts_.append( + float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3])); + } + } + } + + constexpr int point_resolution = 4; + for (auto axis : IndexRange(3)) { + for (auto edge : IndexRange(point_resolution)) { + for (auto vert : IndexRange(2)) { + const float angle = (2 * M_PI) * (edge + vert) / float(point_resolution); + float point[3] = {cosf(angle), sinf(angle), 0.0f}; + point_verts_.append( + float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3])); + } + } + } +}; + +void DebugDraw::init() +{ + cpu_print_buf_.command.v_count = 0; + cpu_print_buf_.command.v_first = 0; + cpu_print_buf_.command.i_count = 1; + cpu_print_buf_.command.i_first = 0; + + cpu_draw_buf_.command.v_count = 0; + cpu_draw_buf_.command.v_first = 0; + cpu_draw_buf_.command.i_count = 1; + cpu_draw_buf_.command.i_first = 0; + + gpu_print_buf_.command.v_count = 0; + gpu_print_buf_.command.v_first = 0; + gpu_print_buf_.command.i_count = 1; + gpu_print_buf_.command.i_first = 0; + gpu_print_buf_used = false; + + gpu_draw_buf_.command.v_count = 0; + gpu_draw_buf_.command.v_first = 0; + gpu_draw_buf_.command.i_count = 1; + gpu_draw_buf_.command.i_first = 0; + gpu_draw_buf_used = false; + + modelmat_reset(); +} + +void DebugDraw::modelmat_reset() +{ + model_mat_ = float4x4::identity(); +} + +void DebugDraw::modelmat_set(const float modelmat[4][4]) +{ + model_mat_ = modelmat; +} + +GPUStorageBuf *DebugDraw::gpu_draw_buf_get() +{ + BLI_assert(GPU_shader_storage_buffer_objects_support()); + if (!gpu_draw_buf_used) { + gpu_draw_buf_used = true; + gpu_draw_buf_.push_update(); + } + return gpu_draw_buf_; +} + +GPUStorageBuf *DebugDraw::gpu_print_buf_get() +{ + BLI_assert(GPU_shader_storage_buffer_objects_support()); + if (!gpu_print_buf_used) { + gpu_print_buf_used = true; + gpu_print_buf_.push_update(); + } + return gpu_print_buf_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw functions + * \{ */ + +void DebugDraw::draw_line(float3 v1, float3 v2, float4 color) +{ + draw_line(v1, v2, color_pack(color)); +} + +void DebugDraw::draw_polygon(Span<float3> poly_verts, float4 color) +{ + BLI_assert(!poly_verts.is_empty()); + + uint col = color_pack(color); + float3 v0 = model_mat_ * poly_verts.last(); + for (auto vert : poly_verts) { + float3 v1 = model_mat_ * vert; + draw_line(v0, v1, col); + v0 = v1; + } +} + +void DebugDraw::draw_matrix(const float4x4 m4) +{ + float3 v0 = float3(0.0f, 0.0f, 0.0f); + float3 v1 = float3(1.0f, 0.0f, 0.0f); + float3 v2 = float3(0.0f, 1.0f, 0.0f); + float3 v3 = float3(0.0f, 0.0f, 1.0f); + + mul_project_m4_v3(m4.ptr(), v0); + mul_project_m4_v3(m4.ptr(), v1); + mul_project_m4_v3(m4.ptr(), v2); + mul_project_m4_v3(m4.ptr(), v3); + + draw_line(v0, v1, float4(1.0f, 0.0f, 0.0f, 1.0f)); + draw_line(v0, v2, float4(0.0f, 1.0f, 0.0f, 1.0f)); + draw_line(v0, v3, float4(0.0f, 0.0f, 1.0f, 1.0f)); +} + +void DebugDraw::draw_bbox(const BoundBox &bbox, const float4 color) +{ + uint col = color_pack(color); + draw_line(bbox.vec[0], bbox.vec[1], col); + draw_line(bbox.vec[1], bbox.vec[2], col); + draw_line(bbox.vec[2], bbox.vec[3], col); + draw_line(bbox.vec[3], bbox.vec[0], col); + + draw_line(bbox.vec[4], bbox.vec[5], col); + draw_line(bbox.vec[5], bbox.vec[6], col); + draw_line(bbox.vec[6], bbox.vec[7], col); + draw_line(bbox.vec[7], bbox.vec[4], col); + + draw_line(bbox.vec[0], bbox.vec[4], col); + draw_line(bbox.vec[1], bbox.vec[5], col); + draw_line(bbox.vec[2], bbox.vec[6], col); + draw_line(bbox.vec[3], bbox.vec[7], col); +} + +void DebugDraw::draw_matrix_as_bbox(float4x4 mat, const float4 color) +{ + BoundBox bb; + const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; + BKE_boundbox_init_from_minmax(&bb, min, max); + for (auto i : IndexRange(8)) { + mul_project_m4_v3(mat.ptr(), bb.vec[i]); + } + draw_bbox(bb, color); +} + +void DebugDraw::draw_sphere(const float3 center, float radius, const float4 color) +{ + uint col = color_pack(color); + for (auto i : IndexRange(sphere_verts_.size() / 2)) { + float3 v0 = sphere_verts_[i * 2] * radius + center; + float3 v1 = sphere_verts_[i * 2 + 1] * radius + center; + draw_line(v0, v1, col); + } +} + +void DebugDraw::draw_point(const float3 center, float radius, const float4 color) +{ + uint col = color_pack(color); + for (auto i : IndexRange(point_verts_.size() / 2)) { + float3 v0 = point_verts_[i * 2] * radius + center; + float3 v1 = point_verts_[i * 2 + 1] * radius + center; + draw_line(v0, v1, col); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Print functions + * \{ */ + +template<> void DebugDraw::print_value<uint>(const uint &value) +{ + print_value_uint(value, false, false, true); +} +template<> void DebugDraw::print_value<int>(const int &value) +{ + print_value_uint(uint(abs(value)), false, (value < 0), false); +} +template<> void DebugDraw::print_value<bool>(const bool &value) +{ + print_string(value ? "true " : "false"); +} +template<> void DebugDraw::print_value<float>(const float &val) +{ + std::stringstream ss; + ss << std::setw(12) << std::to_string(val); + print_string(ss.str()); +} +template<> void DebugDraw::print_value<double>(const double &val) +{ + print_value(float(val)); +} + +template<> void DebugDraw::print_value_hex<uint>(const uint &value) +{ + print_value_uint(value, true, false, false); +} +template<> void DebugDraw::print_value_hex<int>(const int &value) +{ + print_value_uint(uint(value), true, false, false); +} +template<> void DebugDraw::print_value_hex<float>(const float &value) +{ + print_value_uint(*reinterpret_cast<const uint *>(&value), true, false, false); +} +template<> void DebugDraw::print_value_hex<double>(const double &val) +{ + print_value_hex(float(val)); +} + +template<> void DebugDraw::print_value_binary<uint>(const uint &value) +{ + print_value_binary(value); +} +template<> void DebugDraw::print_value_binary<int>(const int &value) +{ + print_value_binary(uint(value)); +} +template<> void DebugDraw::print_value_binary<float>(const float &value) +{ + print_value_binary(*reinterpret_cast<const uint *>(&value)); +} +template<> void DebugDraw::print_value_binary<double>(const double &val) +{ + print_value_binary(float(val)); +} + +template<> void DebugDraw::print_value<float2>(const float2 &value) +{ + print_no_endl("float2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<float3>(const float3 &value) +{ + print_no_endl("float3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<float4>(const float4 &value) +{ + print_no_endl("float4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +template<> void DebugDraw::print_value<int2>(const int2 &value) +{ + print_no_endl("int2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<int3>(const int3 &value) +{ + print_no_endl("int3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<int4>(const int4 &value) +{ + print_no_endl("int4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +template<> void DebugDraw::print_value<uint2>(const uint2 &value) +{ + print_no_endl("uint2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<uint3>(const uint3 &value) +{ + print_no_endl("uint3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<uint4>(const uint4 &value) +{ + print_no_endl("uint4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internals + * + * IMPORTANT: All of these are copied from the shader libs (common_debug_draw_lib.glsl & + * common_debug_print_lib.glsl). They need to be kept in sync to write the same data. + * \{ */ + +void DebugDraw::draw_line(float3 v1, float3 v2, uint color) +{ + DebugDrawBuf &buf = cpu_draw_buf_; + uint index = buf.command.v_count; + if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) { + buf.verts[index + 0] = vert_pack(model_mat_ * v1, color); + buf.verts[index + 1] = vert_pack(model_mat_ * v2, color); + buf.command.v_count += 2; + } +} + +/* Keep in sync with drw_debug_color_pack(). */ +uint DebugDraw::color_pack(float4 color) +{ + color = math::clamp(color, 0.0f, 1.0f); + uint result = 0; + result |= uint(color.x * 255.0f) << 0u; + result |= uint(color.y * 255.0f) << 8u; + result |= uint(color.z * 255.0f) << 16u; + result |= uint(color.w * 255.0f) << 24u; + return result; +} + +DRWDebugVert DebugDraw::vert_pack(float3 pos, uint color) +{ + DRWDebugVert vert; + vert.pos0 = *reinterpret_cast<uint32_t *>(&pos.x); + vert.pos1 = *reinterpret_cast<uint32_t *>(&pos.y); + vert.pos2 = *reinterpret_cast<uint32_t *>(&pos.z); + vert.color = color; + return vert; +} + +void DebugDraw::print_newline() +{ + print_col_ = 0u; + print_row_ = ++cpu_print_buf_.command.i_first; +} + +void DebugDraw::print_string_start(uint len) +{ + /* Break before word. */ + if (print_col_ + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + print_newline(); + } +} + +/* Copied from gpu_shader_dependency. */ +void DebugDraw::print_string(std::string str) +{ + size_t len_before_pad = str.length(); + /* Pad string to uint size to avoid out of bound reads. */ + while (str.length() % 4 != 0) { + str += " "; + } + + print_string_start(len_before_pad); + for (size_t i = 0; i < len_before_pad; i += 4) { + union { + uint8_t chars[4]; + uint32_t word; + }; + + chars[0] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0); + chars[1] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1); + chars[2] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2); + chars[3] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3); + + if (i + 4 > len_before_pad) { + chars[len_before_pad - i] = '\0'; + } + print_char4(word); + } +} + +/* Keep in sync with shader. */ +void DebugDraw::print_char4(uint data) +{ + /* Convert into char stream. */ + for (; data != 0u; data >>= 8u) { + uint char1 = data & 0xFFu; + /* Check for null terminator. */ + if (char1 == 0x00) { + break; + } + /* NOTE: Do not skip the header manually like in GPU. */ + uint cursor = cpu_print_buf_.command.v_count++; + if (cursor < DRW_DEBUG_PRINT_MAX) { + /* For future usage. (i.e: Color) */ + uint flags = 0u; + uint col = print_col_++; + uint print_header = (flags << 24u) | (print_row_ << 16u) | (col << 8u); + cpu_print_buf_.char_array[cursor] = print_header | char1; + /* Break word. */ + if (print_col_ > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + print_newline(); + } + } + } +} + +void DebugDraw::print_append_char(uint char1, uint &char4) +{ + char4 = (char4 << 8u) | char1; +} + +void DebugDraw::print_append_digit(uint digit, uint &char4) +{ + const uint char_A = 0x41u; + const uint char_0 = 0x30u; + bool is_hexadecimal = digit > 9u; + char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit)); +} + +void DebugDraw::print_append_space(uint &char4) +{ + char4 = (char4 << 8u) | 0x20u; +} + +void DebugDraw::print_value_binary(uint value) +{ + print_string("0b"); + print_string_start(10u * 4u); + uint digits[10] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; + uint digit = 0u; + for (uint i = 0u; i < 32u; i++) { + print_append_digit(((value >> i) & 1u), digits[digit / 4u]); + digit++; + if ((i % 4u) == 3u) { + print_append_space(digits[digit / 4u]); + digit++; + } + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 9; j >= 0; j--) { + print_char4(digits[j]); + } +} + +void DebugDraw::print_value_uint(uint value, + const bool hex, + bool is_negative, + const bool is_unsigned) +{ + print_string_start(3u * 4u); + const uint blank_value = hex ? 0x30303030u : 0x20202020u; + const uint prefix = hex ? 0x78302020u : 0x20202020u; + uint digits[3] = {blank_value, blank_value, prefix}; + const uint base = hex ? 16u : 10u; + uint digit = 0u; + /* Add `u` suffix. */ + if (is_unsigned) { + print_append_char('u', digits[digit / 4u]); + digit++; + } + /* Number's digits. */ + for (; value != 0u || digit == uint(is_unsigned); value /= base) { + print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Add negative sign. */ + if (is_negative) { + print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + print_char4(digits[j]); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display + * \{ */ + +void DebugDraw::display_lines() +{ + if (cpu_draw_buf_.command.v_count == 0 && gpu_draw_buf_used == false) { + return; + } + GPU_debug_group_begin("Lines"); + cpu_draw_buf_.push_update(); + + float4x4 persmat; + const DRWView *view = DRW_view_get_active(); + DRW_view_persmat_get(view, persmat.ptr(), false); + + drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); + + GPUBatch *batch = drw_cache_procedural_lines_get(); + GPUShader *shader = DRW_shader_debug_draw_display_get(); + GPU_batch_set_shader(batch, shader); + int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS); + GPU_shader_uniform_mat4(shader, "persmat", persmat.ptr()); + + if (gpu_draw_buf_used) { + GPU_debug_group_begin("GPU"); + GPU_storagebuf_bind(gpu_draw_buf_, slot); + GPU_batch_draw_indirect(batch, gpu_draw_buf_); + GPU_storagebuf_unbind(gpu_draw_buf_); + GPU_debug_group_end(); + } + + GPU_debug_group_begin("CPU"); + GPU_storagebuf_bind(cpu_draw_buf_, slot); + GPU_batch_draw_indirect(batch, cpu_draw_buf_); + GPU_storagebuf_unbind(cpu_draw_buf_); + GPU_debug_group_end(); + + GPU_debug_group_end(); +} + +void DebugDraw::display_prints() +{ + if (cpu_print_buf_.command.v_count == 0 && gpu_print_buf_used == false) { + return; + } + GPU_debug_group_begin("Prints"); + cpu_print_buf_.push_update(); + + drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_PROGRAM_POINT_SIZE); + + GPUBatch *batch = drw_cache_procedural_points_get(); + GPUShader *shader = DRW_shader_debug_print_display_get(); + GPU_batch_set_shader(batch, shader); + int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); + + if (gpu_print_buf_used) { + GPU_debug_group_begin("GPU"); + GPU_storagebuf_bind(gpu_print_buf_, slot); + GPU_batch_draw_indirect(batch, gpu_print_buf_); + GPU_storagebuf_unbind(gpu_print_buf_); + GPU_debug_group_end(); + } + + GPU_debug_group_begin("CPU"); + GPU_storagebuf_bind(cpu_print_buf_, slot); + GPU_batch_draw_indirect(batch, cpu_print_buf_); + GPU_storagebuf_unbind(cpu_print_buf_); + GPU_debug_group_end(); + + GPU_debug_group_end(); +} + +void DebugDraw::display_to_view() +{ + GPU_debug_group_begin("DebugDraw"); + + display_lines(); + /* Print 3D shapes before text to avoid overlaps. */ + display_prints(); + /* Init again so we don't draw the same thing twice. */ + init(); + + GPU_debug_group_end(); +} + +} // namespace blender::draw + +blender::draw::DebugDraw *DRW_debug_get() +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return nullptr; + } + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API private + * \{ */ + +void drw_debug_draw() +{ +#ifdef DEBUG + if (!GPU_shader_storage_buffer_objects_support() || DST.debug == nullptr) { + return; + } + /* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */ + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->display_to_view(); +#endif +} + +/** + * NOTE: Init is once per draw manager cycle. + */ +void drw_debug_init() +{ + /* Module should not be used in release builds. */ + /* TODO(@fclem): Hide the functions declarations without using `ifdefs` everywhere. */ +#ifdef DEBUG + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + /* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */ + if (DST.debug == nullptr) { + DST.debug = reinterpret_cast<DRWDebugModule *>(new blender::draw::DebugDraw()); + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->init(); +#endif +} + +void drw_debug_module_free(DRWDebugModule *module) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + if (module != nullptr) { + delete reinterpret_cast<blender::draw::DebugDraw *>(module); + } +} + +GPUStorageBuf *drw_debug_gpu_draw_buf_get() +{ + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_draw_buf_get(); +} + +GPUStorageBuf *drw_debug_gpu_print_buf_get() +{ + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_print_buf_get(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API public + * \{ */ + +void DRW_debug_modelmat_reset() +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_reset(); +} + +void DRW_debug_modelmat(const float modelmat[4][4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_set(modelmat); +} + +void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_line(v1, v2, color); +} + +void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_polygon( + blender::Span<float3>((float3 *)v, vert_len), color); +} + +void DRW_debug_m4(const float m[4][4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix(m); +} + +void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + blender::float4x4 m4 = m; + if (invert) { + m4 = m4.inverted(); + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix_as_bbox(m4, color); +} + +void DRW_debug_bbox(const BoundBox *bbox, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_bbox(*bbox, color); +} + +void DRW_debug_sphere(const float center[3], float radius, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_sphere(center, radius, color); +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_debug.h b/source/blender/draw/intern/draw_debug.h index 333d734edb9..9a56a12242e 100644 --- a/source/blender/draw/intern/draw_debug.h +++ b/source/blender/draw/intern/draw_debug.h @@ -3,21 +3,38 @@ /** \file * \ingroup draw + * + * \brief Simple API to draw debug shapes in the viewport. + * IMPORTANT: This is the legacy API for C. Use draw_debug.hh instead in new C++ code. */ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DRWDebugModule DRWDebugModule; + struct BoundBox; void DRW_debug_modelmat_reset(void); void DRW_debug_modelmat(const float modelmat[4][4]); +/** + * IMPORTANT: For now there is a limit of DRW_DEBUG_DRAW_VERT_MAX that can be drawn + * using all the draw functions. + */ void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]); void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4]); /** * \note g_modelmat is still applied on top. */ void DRW_debug_m4(const float m[4][4]); -void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], bool invert); +void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4]); void DRW_debug_bbox(const BoundBox *bbox, const float color[4]); void DRW_debug_sphere(const float center[3], float radius, const float color[4]); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_debug.hh b/source/blender/draw/intern/draw_debug.hh new file mode 100644 index 00000000000..730b69ed954 --- /dev/null +++ b/source/blender/draw/intern/draw_debug.hh @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw + * + * \brief Simple API to draw debug shapes and log in the viewport. + * + * Both CPU and GPU implementation are supported and symmetrical (meaning GPU shader can use it + * too, see common_debug_print/draw_lib.glsl). + * + * NOTE: CPU logging will overlap GPU logging on screen as it is drawn after. + */ + +#pragma once + +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" +#include "DNA_object_types.h" +#include "DRW_gpu_wrapper.hh" + +namespace blender::draw { + +/* Shortcuts to avoid boilerplate code and match shader API. */ +#define drw_debug_line(...) DRW_debug_get()->draw_line(__VA_ARGS__) +#define drw_debug_polygon(...) DRW_debug_get()->draw_polygon(__VA_ARGS__) +#define drw_debug_bbox(...) DRW_debug_get()->draw_bbox(__VA_ARGS__) +#define drw_debug_sphere(...) DRW_debug_get()->draw_sphere(__VA_ARGS__) +#define drw_debug_point(...) DRW_debug_get()->draw_point(__VA_ARGS__) +#define drw_debug_matrix(...) DRW_debug_get()->draw_matrix(__VA_ARGS__) +#define drw_debug_matrix_as_bbox(...) DRW_debug_get()->draw_matrix_as_bbox(__VA_ARGS__) +#define drw_print(...) DRW_debug_get()->print(__VA_ARGS__) +#define drw_print_hex(...) DRW_debug_get()->print_hex(__VA_ARGS__) +#define drw_print_binary(...) DRW_debug_get()->print_binary(__VA_ARGS__) +#define drw_print_no_endl(...) DRW_debug_get()->print_no_endl(__VA_ARGS__) + +/* Will log variable along with its name, like the shader version of print(). */ +#define drw_print_id(v_) DRW_debug_get()->print(#v_, "= ", v_) +#define drw_print_id_no_endl(v_) DRW_debug_get()->print_no_endl(#v_, "= ", v_) + +class DebugDraw { + private: + using DebugDrawBuf = StorageBuffer<DRWDebugDrawBuffer>; + using DebugPrintBuf = StorageBuffer<DRWDebugPrintBuffer>; + + /** Data buffers containing all verts or chars to draw. */ + DebugDrawBuf cpu_draw_buf_ = {"DebugDrawBuf-CPU"}; + DebugDrawBuf gpu_draw_buf_ = {"DebugDrawBuf-GPU"}; + DebugPrintBuf cpu_print_buf_ = {"DebugPrintBuf-CPU"}; + DebugPrintBuf gpu_print_buf_ = {"DebugPrintBuf-GPU"}; + /** True if the gpu buffer have been requested and may contain data to draw. */ + bool gpu_print_buf_used = false; + bool gpu_draw_buf_used = false; + /** Matrix applied to all points before drawing. Could be a stack if needed. */ + float4x4 model_mat_; + /** Precomputed shapes verts. */ + Vector<float3> sphere_verts_; + Vector<float3> point_verts_; + /** Cursor position for print functionality. */ + uint print_col_ = 0; + uint print_row_ = 0; + + public: + DebugDraw(); + ~DebugDraw(){}; + + /** + * Resets all buffers and reset model matrix state. + * Not to be called by user. + */ + void init(); + + /** + * Resets model matrix state to identity. + */ + void modelmat_reset(); + /** + * Sets model matrix transform to apply to any vertex passed to drawing functions. + */ + void modelmat_set(const float modelmat[4][4]); + + /** + * Drawing functions that will draw wire-frames with the given color. + */ + void draw_line(float3 v1, float3 v2, float4 color = {1, 0, 0, 1}); + void draw_polygon(Span<float3> poly_verts, float4 color = {1, 0, 0, 1}); + void draw_bbox(const BoundBox &bbox, const float4 color = {1, 0, 0, 1}); + void draw_sphere(const float3 center, float radius, const float4 color = {1, 0, 0, 1}); + void draw_point(const float3 center, float radius = 0.01f, const float4 color = {1, 0, 0, 1}); + /** + * Draw a matrix transformation as 3 colored axes. + */ + void draw_matrix(const float4x4 m4); + /** + * Draw a matrix as a 2 units length bounding box, centered on origin. + */ + void draw_matrix_as_bbox(float4x4 mat, const float4 color = {1, 0, 0, 1}); + + /** + * Will draw all debug shapes and text cached up until now to the current view / frame-buffer. + * Draw buffers will be emptied and ready for new debug data. + */ + void display_to_view(); + + /** + * Log variable or strings inside the viewport. + * Using a unique non string argument will print the variable name with it. + * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`. + */ + template<typename... Ts> void print(StringRefNull str, Ts... args) + { + print_no_endl(str, args...); + print_newline(); + } + template<typename T> void print(const T &&value) + { + print_value(value); + print_newline(); + } + template<typename T> void print_hex(const T &&value) + { + print_value_hex(value); + print_newline(); + } + template<typename T> void print_binary(const T &&value) + { + print_value_binary(value); + print_newline(); + } + + /** + * Same as `print()` but does not finish the line. + */ + void print_no_endl(std::string arg) + { + print_string(arg); + } + void print_no_endl(StringRef arg) + { + print_string(arg); + } + void print_no_endl(StringRefNull arg) + { + print_string(arg); + } + void print_no_endl(char const *arg) + { + print_string(StringRefNull(arg)); + } + template<typename T> void print_no_endl(T arg) + { + print_value(arg); + } + template<typename T, typename... Ts> void print_no_endl(T arg, Ts... args) + { + print_no_endl(arg); + print_no_endl(args...); + } + + /** + * Not to be called by user. Should become private. + */ + GPUStorageBuf *gpu_draw_buf_get(); + GPUStorageBuf *gpu_print_buf_get(); + + private: + uint color_pack(float4 color); + DRWDebugVert vert_pack(float3 pos, uint color); + + void draw_line(float3 v1, float3 v2, uint color); + + void print_newline(); + void print_string_start(uint len); + void print_string(std::string str); + void print_char4(uint data); + void print_append_char(uint char1, uint &char4); + void print_append_digit(uint digit, uint &char4); + void print_append_space(uint &char4); + void print_value_binary(uint value); + void print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned); + + template<typename T> void print_value(const T &value); + template<typename T> void print_value_hex(const T &value); + template<typename T> void print_value_binary(const T &value); + + void display_lines(); + void display_prints(); +}; + +} // namespace blender::draw + +/** + * Ease of use function to get the debug module. + * TODO(fclem): Should be removed once DRWManager is no longer global. + * IMPORTANT: Can return nullptr if storage buffer is not supported. + */ +blender::draw::DebugDraw *DRW_debug_get(); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index b2422504825..4693e5f8e20 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -43,6 +43,7 @@ #include "DNA_camera_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_userdef_types.h" #include "DNA_world_types.h" #include "ED_gpencil.h" @@ -84,6 +85,7 @@ #include "draw_cache_impl.h" #include "engines/basic/basic_engine.h" +#include "engines/compositor/compositor_engine.h" #include "engines/eevee/eevee_engine.h" #include "engines/eevee_next/eevee_engine.h" #include "engines/external/external_engine.h" @@ -1214,6 +1216,31 @@ static void drw_engines_enable_editors(void) } } +static bool is_compositor_enabled(void) +{ + if (!U.experimental.use_realtime_compositor) { + return false; + } + + if (!(DST.draw_ctx.v3d->shading.flag & V3D_SHADING_COMPOSITOR)) { + return false; + } + + if (!(DST.draw_ctx.v3d->shading.type >= OB_MATERIAL)) { + return false; + } + + if (!DST.draw_ctx.scene->use_nodes) { + return false; + } + + if (!DST.draw_ctx.scene->nodetree) { + return false; + } + + return true; +} + static void drw_engines_enable(ViewLayer *UNUSED(view_layer), RenderEngineType *engine_type, bool gpencil_engine_needed) @@ -1226,6 +1253,11 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer), if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) { use_drw_engine(&draw_engine_gpencil_type); } + + if (is_compositor_enabled()) { + use_drw_engine(&draw_engine_compositor_type); + } + drw_engines_enable_overlays(); #ifdef WITH_DRAW_DEBUG @@ -1597,7 +1629,6 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, GPUViewport *viewport, const bContext *evil_C) { - Scene *scene = DEG_get_evaluated_scene(depsgraph); ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); RegionView3D *rv3d = region->regiondata; @@ -2948,6 +2979,7 @@ void DRW_engines_register(void) DRW_engine_register(&draw_engine_overlay_type); DRW_engine_register(&draw_engine_select_type); DRW_engine_register(&draw_engine_basic_type); + DRW_engine_register(&draw_engine_compositor_type); #ifdef WITH_DRAW_DEBUG DRW_engine_register(&draw_engine_debug_select_type); #endif @@ -3028,6 +3060,9 @@ void DRW_engines_free(void) DRW_stats_free(); DRW_globals_free(); + drw_debug_module_free(DST.debug); + DST.debug = NULL; + DRW_UBO_FREE_SAFE(G_draw.block_ubo); DRW_UBO_FREE_SAFE(G_draw.view_ubo); DRW_TEXTURE_FREE_SAFE(G_draw.ramp); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 411123f00bf..a29f2fa7507 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -501,20 +501,6 @@ typedef struct DRWCommandSmallChunk { BLI_STATIC_ASSERT_ALIGN(DRWCommandChunk, 16); #endif -/* ------------- DRAW DEBUG ------------ */ - -typedef struct DRWDebugLine { - struct DRWDebugLine *next; /* linked list */ - float pos[2][3]; - float color[4]; -} DRWDebugLine; - -typedef struct DRWDebugSphere { - struct DRWDebugSphere *next; /* linked list */ - float mat[4][4]; - float color[4]; -} DRWDebugSphere; - /* ------------- Memory Pools ------------ */ /* Contains memory pools information */ @@ -656,11 +642,7 @@ typedef struct DRWManager { GPUDrawList *draw_list; - struct { - /* TODO(@fclem): optimize: use chunks. */ - DRWDebugLine *lines; - DRWDebugSphere *spheres; - } debug; + DRWDebugModule *debug; } DRWManager; extern DRWManager DST; /* TODO: get rid of this and allow multi-threaded rendering. */ @@ -675,6 +657,9 @@ void drw_state_set(DRWState state); void drw_debug_draw(void); void drw_debug_init(void); +void drw_debug_module_free(DRWDebugModule *module); +GPUStorageBuf *drw_debug_gpu_draw_buf_get(void); +GPUStorageBuf *drw_debug_gpu_print_buf_get(void); eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 9392efcf92b..9d4a2c58ab6 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -17,9 +17,14 @@ #include "BKE_pbvh.h" #include "BKE_volume.h" +/* For debug cursor position. */ +#include "WM_api.h" +#include "wm_window.h" + #include "DNA_curve_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_screen_types.h" #include "BLI_alloca.h" #include "BLI_hash.h" @@ -39,6 +44,16 @@ #include "intern/gpu_codegen.h" +/** + * IMPORTANT: + * In order to be able to write to the same print buffer sequentially, we add a barrier to allow + * multiple shader calls writing to the same buffer. + * However, this adds explicit synchronization events which might change the rest of the + * application behavior and hide some bugs. If you know you are using shader debug print in only + * one shader pass, you can comment this out to remove the aforementioned barrier. + */ +#define DISABLE_DEBUG_SHADER_PRINT_BARRIER + /* -------------------------------------------------------------------- */ /** \name Uniform Buffer Object (DRW_uniformbuffer) * \{ */ @@ -1510,6 +1525,27 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) shgroup, view_ubo_location, DRW_UNIFORM_BLOCK, G_draw.view_ubo, 0, 0, 1); } +#ifdef DEBUG + int debug_print_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); + if (debug_print_location != -1) { + GPUStorageBuf *buf = drw_debug_gpu_print_buf_get(); + drw_shgroup_uniform_create_ex( + shgroup, debug_print_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1); +# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER + /* Add a barrier to allow multiple shader writing to the same buffer. */ + DRW_shgroup_barrier(shgroup, GPU_BARRIER_SHADER_STORAGE); +# endif + } + + int debug_draw_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS); + if (debug_draw_location != -1) { + GPUStorageBuf *buf = drw_debug_gpu_draw_buf_get(); + drw_shgroup_uniform_create_ex( + shgroup, debug_draw_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1); + /* NOTE(fclem): No barrier as ordering is not important. */ + } +#endif + /* Not supported. */ BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV) == -1); BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW) == -1); @@ -1986,6 +2022,13 @@ DRWView *DRW_view_create(const float viewmat[4][4], copy_v4_fl4(view->storage.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f); + if (DST.draw_ctx.evil_C && DST.draw_ctx.region) { + int region_origin[2] = {DST.draw_ctx.region->winrct.xmin, DST.draw_ctx.region->winrct.ymin}; + struct wmWindow *win = CTX_wm_window(DST.draw_ctx.evil_C); + wm_cursor_position_get(win, &view->storage.mouse_pixel[0], &view->storage.mouse_pixel[1]); + sub_v2_v2v2_int(view->storage.mouse_pixel, view->storage.mouse_pixel, region_origin); + } + DRW_view_update(view, viewmat, winmat, culling_viewmat, culling_winmat); return view; diff --git a/source/blender/draw/intern/draw_shader.cc b/source/blender/draw/intern/draw_shader.cc index 001ceb0ae8d..ecb30d54b64 100644 --- a/source/blender/draw/intern/draw_shader.cc +++ b/source/blender/draw/intern/draw_shader.cc @@ -24,6 +24,8 @@ extern "C" char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static struct { struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; + struct GPUShader *debug_print_display_sh; + struct GPUShader *debug_draw_display_sh; } e_data = {{nullptr}}; /* -------------------------------------------------------------------- */ @@ -109,6 +111,22 @@ GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineSh return e_data.hair_refine_sh[type]; } +GPUShader *DRW_shader_debug_print_display_get() +{ + if (e_data.debug_print_display_sh == nullptr) { + e_data.debug_print_display_sh = GPU_shader_create_from_info_name("draw_debug_print_display"); + } + return e_data.debug_print_display_sh; +} + +GPUShader *DRW_shader_debug_draw_display_get() +{ + if (e_data.debug_draw_display_sh == nullptr) { + e_data.debug_draw_display_sh = GPU_shader_create_from_info_name("draw_debug_draw_display"); + } + return e_data.debug_draw_display_sh; +} + /** \} */ void DRW_shaders_free() @@ -116,4 +134,6 @@ void DRW_shaders_free() for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); } + DRW_SHADER_FREE_SAFE(e_data.debug_print_display_sh); + DRW_SHADER_FREE_SAFE(e_data.debug_draw_display_sh); } diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index 63d755cc334..dabb4b3327f 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -30,6 +30,9 @@ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, struct GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineShaderType sh_type); +struct GPUShader *DRW_shader_debug_print_display_get(void); +struct GPUShader *DRW_shader_debug_draw_display_get(void); + void DRW_shaders_free(void); #ifdef __cplusplus diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index fbcc78e52f9..90a6475c42b 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -14,6 +14,9 @@ typedef struct CurvesInfos CurvesInfos; typedef struct DrawCommand DrawCommand; typedef struct DrawCommandIndexed DrawCommandIndexed; typedef struct DispatchCommand DispatchCommand; +typedef struct DRWDebugPrintBuffer DRWDebugPrintBuffer; +typedef struct DRWDebugVert DRWDebugVert; +typedef struct DRWDebugDrawBuffer DRWDebugDrawBuffer; #endif #define DRW_SHADER_SHARED_H @@ -48,6 +51,12 @@ struct ViewInfos { /** NOTE: vec3 arrays are padded to vec4. */ float4 frustum_corners[8]; float4 frustum_planes[6]; + + /** For debugging purpose */ + /* Mouse pixel. */ + int2 mouse_pixel; + + int2 _pad0; }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) @@ -131,3 +140,59 @@ struct DispatchCommand { uint _pad0; }; BLI_STATIC_ASSERT_ALIGN(DispatchCommand, 16) + +/* -------------------------------------------------------------------- */ +/** \name Debug print + * \{ */ + +/* Take the header (DrawCommand) into account. */ +#define DRW_DEBUG_PRINT_MAX (8 * 1024) - 4 +/* NOTE: Cannot be more than 255 (because of column encoding). */ +#define DRW_DEBUG_PRINT_WORD_WRAP_COLUMN 120u + +/* The debug print buffer is laid-out as the following struct. + * But we use plain array in shader code instead because of driver issues. */ +struct DRWDebugPrintBuffer { + DrawCommand command; + /** Each character is encoded as 3 `uchar` with char_index, row and column position. */ + uint char_array[DRW_DEBUG_PRINT_MAX]; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16) + +/* Use number of char as vertex count. Equivalent to `DRWDebugPrintBuffer.command.v_count`. */ +#define drw_debug_print_cursor drw_debug_print_buf[0] +/* Reuse first instance as row index as we don't use instancing. Equivalent to + * `DRWDebugPrintBuffer.command.i_first`. */ +#define drw_debug_print_row_shared drw_debug_print_buf[3] + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug draw shapes + * \{ */ + +struct DRWDebugVert { + /* This is a weird layout, but needed to be able to use DRWDebugVert as + * a DrawCommand and avoid alignment issues. See drw_debug_verts_buf[] definition. */ + uint pos0; + uint pos1; + uint pos2; + uint color; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugVert, 16) + +/* Take the header (DrawCommand) into account. */ +#define DRW_DEBUG_DRAW_VERT_MAX (64 * 1024) - 1 + +/* The debug draw buffer is laid-out as the following struct. + * But we use plain array in shader code instead because of driver issues. */ +struct DRWDebugDrawBuffer { + DrawCommand command; + DRWDebugVert verts[DRW_DEBUG_DRAW_VERT_MAX]; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16) + +/* Equivalent to `DRWDebugDrawBuffer.command.v_count`. */ +#define drw_debug_draw_v_count drw_debug_verts_buf[0].pos0 + +/** \} */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh index a4773736f0c..57abf088c16 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh @@ -83,6 +83,9 @@ struct MeshRenderData { MLoopTri *mlooptri; const float (*vert_normals)[3]; const float (*poly_normals)[3]; + const bool *hide_vert; + const bool *hide_edge; + const bool *hide_poly; float (*loop_normals)[3]; int *lverts, *ledges; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index 2ff093d0bd8..9f82cc56941 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -61,7 +61,7 @@ static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); const MPoly *mp = &mr->mpoly[mlt->poly]; edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, + mr->hide_poly && mr->hide_poly[mlt->poly], (mp->flag & ME_FACE_SEL) != 0, mlt->tri[0], mlt->tri[1], @@ -117,7 +117,7 @@ static void extract_edituv_tris_iter_subdiv_bm(const DRWSubdivCache *UNUSED(subd } static void extract_edituv_tris_iter_subdiv_mesh(const DRWSubdivCache *UNUSED(subdiv_cache), - const MeshRenderData *UNUSED(mr), + const MeshRenderData *mr, void *_data, uint subdiv_quad_index, const MPoly *coarse_quad) @@ -125,19 +125,13 @@ static void extract_edituv_tris_iter_subdiv_mesh(const DRWSubdivCache *UNUSED(su MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); const uint loop_idx = subdiv_quad_index * 4; - edituv_tri_add(data, - (coarse_quad->flag & ME_HIDE) != 0, - (coarse_quad->flag & ME_FACE_SEL) != 0, - loop_idx, - loop_idx + 1, - loop_idx + 2); + const bool hidden = mr->hide_poly && mr->hide_poly[coarse_quad - mr->mpoly]; - edituv_tri_add(data, - (coarse_quad->flag & ME_HIDE) != 0, - (coarse_quad->flag & ME_FACE_SEL) != 0, - loop_idx, - loop_idx + 2, - loop_idx + 3); + edituv_tri_add( + data, hidden, (coarse_quad->flag & ME_FACE_SEL) != 0, loop_idx, loop_idx + 1, loop_idx + 2); + + edituv_tri_add( + data, hidden, (coarse_quad->flag & ME_FACE_SEL) != 0, loop_idx, loop_idx + 2, loop_idx + 3); } static void extract_edituv_tris_finish_subdiv(const struct DRWSubdivCache *UNUSED(subdiv_cache), @@ -218,6 +212,8 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { @@ -227,11 +223,8 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); const bool real_edge = (mr->e_origindex == nullptr || mr->e_origindex[ml->e] != ORIGINDEX_NONE); - edituv_edge_add(data, - (mp->flag & ME_HIDE) != 0 || !real_edge, - (mp->flag & ME_FACE_SEL) != 0, - ml_index, - ml_index_next); + edituv_edge_add( + data, hidden || !real_edge, (mp->flag & ME_FACE_SEL) != 0, ml_index, ml_index_next); } } @@ -288,6 +281,8 @@ static void extract_edituv_lines_iter_subdiv_mesh(const DRWSubdivCache *subdiv_c const MPoly *coarse_poly) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[coarse_poly - mr->mpoly]; + int *subdiv_loop_edge_index = (int *)GPU_vertbuf_get_data(subdiv_cache->edges_orig_index); uint start_loop_idx = subdiv_quad_index * 4; @@ -298,7 +293,7 @@ static void extract_edituv_lines_iter_subdiv_mesh(const DRWSubdivCache *subdiv_c (mr->e_origindex == nullptr || mr->e_origindex[edge_origindex] != ORIGINDEX_NONE)); edituv_edge_add(data, - (coarse_poly->flag & ME_HIDE) != 0 || !real_edge, + hidden || !real_edge, (coarse_poly->flag & ME_FACE_SEL) != 0, loop_idx, (loop_idx + 1 == end_loop_idx) ? start_loop_idx : (loop_idx + 1)); @@ -382,14 +377,15 @@ static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; const bool real_vert = !mr->v_origindex || mr->v_origindex[ml->v] != ORIGINDEX_NONE; - edituv_point_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); + edituv_point_add(data, hidden || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); } } @@ -442,6 +438,7 @@ static void extract_edituv_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_ const MPoly *coarse_quad) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[coarse_quad - mr->mpoly]; int *subdiv_loop_vert_index = (int *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index); uint start_loop_idx = subdiv_quad_index * 4; @@ -450,10 +447,7 @@ static void extract_edituv_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_ const int vert_origindex = subdiv_loop_vert_index[i]; const bool real_vert = !mr->v_origindex || (vert_origindex != -1 && mr->v_origindex[vert_origindex] != ORIGINDEX_NONE); - edituv_point_add(data, - ((coarse_quad->flag & ME_HIDE) != 0) || !real_vert, - (coarse_quad->flag & ME_FACE_SEL) != 0, - i); + edituv_point_add(data, hidden || !real_vert, (coarse_quad->flag & ME_FACE_SEL) != 0, i); } } @@ -533,6 +527,8 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + if (mr->use_subsurf_fdots) { const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags; @@ -543,16 +539,13 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE); const bool subd_fdot = BLI_BITMAP_TEST(facedot_tags, ml->v); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); + edituv_facedot_add( + data, hidden || !real_fdot || !subd_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } } else { const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); + edituv_facedot_add(data, hidden || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc index cc0b383f12b..8dc00617039 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc @@ -42,6 +42,8 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, const int mp_index, void *_userdata) { + const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); if (mr->use_subsurf_fdots) { const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags; @@ -50,7 +52,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - if (BLI_BITMAP_TEST(facedot_tags, ml->v) && !(mr->use_hide && (mp->flag & ME_HIDE))) { + if (BLI_BITMAP_TEST(facedot_tags, ml->v) && !hidden) { GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); return; } @@ -58,7 +60,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, GPU_indexbuf_set_point_restart(elb, mp_index); } else { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + if (!hidden) { GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); } else { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index e6c0d815963..fe883fb0c96 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -58,14 +58,12 @@ static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); /* Using poly & loop iterator would complicate accessing the adjacent loop. */ const MLoop *mloop = mr->mloop; - const MEdge *medge = mr->medge; if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != nullptr)) { const int ml_index_last = mp->loopstart + (mp->totloop - 1); int ml_index = ml_index_last, ml_index_next = mp->loopstart; do { const MLoop *ml = &mloop[ml_index]; - const MEdge *med = &medge[ml->e]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || + if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[ml->e]) || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); @@ -111,7 +109,7 @@ static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); const int l_index_offset = mr->edge_len + ledge_index; const int e_index = mr->ledges[ledge_index]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || + if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[med - mr->medge]) || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { const int l_index = mr->loop_len + ledge_index * 2; @@ -185,9 +183,14 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, switch (mr->extract_type) { case MR_EXTRACT_MESH: { - const MEdge *medge = mr->medge; - for (DRWSubdivLooseEdge edge : loose_edges) { - *flags_data++ = (medge[edge.coarse_edge_index].flag & ME_HIDE) != 0; + const bool *hide_vert = mr->hide_vert; + if (hide_vert) { + for (DRWSubdivLooseEdge edge : loose_edges) { + *flags_data++ = hide_vert[edge.coarse_edge_index]; + } + } + else { + MutableSpan<uint>(flags_data, loose_edges.size()).fill(0); } break; } @@ -199,18 +202,23 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, } } else { - for (DRWSubdivLooseEdge edge : loose_edges) { - int e = edge.coarse_edge_index; - - if (mr->e_origindex && mr->e_origindex[e] != ORIGINDEX_NONE) { - *flags_data++ = (mr->medge[mr->e_origindex[e]].flag & ME_HIDE) != 0; - } - else { - *flags_data++ = false; + const bool *hide_vert = mr->hide_vert; + if (hide_vert) { + for (DRWSubdivLooseEdge edge : loose_edges) { + int e = edge.coarse_edge_index; + + if (mr->e_origindex && mr->e_origindex[e] != ORIGINDEX_NONE) { + *flags_data++ = hide_vert[edge.coarse_edge_index]; + } + else { + *flags_data++ = false; + } } } + else { + MutableSpan<uint>(flags_data, loose_edges.size()).fill(0); + } } - break; } case MR_EXTRACT_BMESH: { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc index c2cfb66ec28..d6c246c51a9 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc @@ -119,16 +119,17 @@ static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data); - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); + const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mlt->poly]; + if (hidden) { + return; } + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); } static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc index 11c71d61775..d5f31c08eaf 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc @@ -47,8 +47,7 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, const MLoop *ml = &mloop[ml_index]; const int e_index = ml->e; - const MEdge *me = &mr->medge[e_index]; - if (!((mr->use_hide && (me->flag & ME_HIDE)) || + if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[e_index]) || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { @@ -122,8 +121,7 @@ static void extract_lines_paint_mask_iter_subdiv_mesh(const DRWSubdivCache *subd GPU_indexbuf_set_line_restart(&data->elb, subdiv_edge_index); } else { - const MEdge *me = &mr->medge[coarse_edge_index]; - if (!((mr->use_hide && (me->flag & ME_HIDE)) || + if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[coarse_edge_index]) || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && (mr->e_origindex[coarse_edge_index] == ORIGINDEX_NONE)))) { const uint ml_index_other = (loop_idx == (end_loop_idx - 1)) ? start_loop_idx : diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc index f7c5505422b..ca46a38823d 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc @@ -43,10 +43,10 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, const int v_index, const int l_index) { - const MVert *mv = &mr->mvert[v_index]; - if (!((mr->use_hide && (mv->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && - (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) { + const bool hidden = mr->use_hide && mr->hide_vert && mr->hide_vert[v_index]; + + if (!(hidden || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && + (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) { GPU_indexbuf_set_point_vert(elb, v_index, l_index); } else { @@ -181,8 +181,7 @@ static void extract_points_iter_subdiv_common(GPUIndexBufBuilder *elb, } } else { - const MVert *mv = &mr->mvert[coarse_vertex_index]; - if (mr->use_hide && (mv->flag & ME_HIDE)) { + if (mr->use_hide && mr->hide_vert && mr->hide_vert[coarse_vertex_index]) { GPU_indexbuf_set_point_restart(elb, coarse_vertex_index); continue; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc index 9fc18620d11..2e3e6c7b6b1 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc @@ -189,12 +189,12 @@ static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr, void *_data) { GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data); - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]); + const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mlt->poly]; + if (hidden) { + GPU_indexbuf_set_tri_restart(elb, mlt_index); } else { - GPU_indexbuf_set_tri_restart(elb, mlt_index); + GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc index ac517269e7d..ef67e1b540d 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc @@ -62,6 +62,8 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, const int mp_index, void *data) { + const bool hidden = mr->hide_poly && mr->hide_poly[mp_index]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { @@ -80,8 +82,8 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, /* Flag for paint mode overlay. * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. * In paint mode it will use the un-mapped data to draw the wire-frame. */ - if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && - (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { + if (hidden || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && + mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { lnor_data->w = -1; } else if (mp->flag & ME_FACE_SEL) { @@ -185,6 +187,8 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, const int mp_index, void *data) { + const bool hidden = mr->hide_poly && mr->hide_poly[mp_index]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { @@ -203,8 +207,8 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, /* Flag for paint mode overlay. * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. * In paint mode it will use the un-mapped data to draw the wire-frame. */ - if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && - (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { + if (hidden || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && + mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { lnor_data->w = -1; } else if (mp->flag & ME_FACE_SEL) { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index 9788beabeb5..313838be9e8 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -83,10 +83,11 @@ static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, const MPoly *mp, - const int UNUSED(mp_index), + const int mp_index, void *_data) { MeshExtract_PosNor_Data *data = static_cast<MeshExtract_PosNor_Data *>(_data); + const bool poly_hidden = mr->hide_poly && mr->hide_poly[mp_index]; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; @@ -95,10 +96,11 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, PosNorLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; + const bool vert_hidden = mr->hide_vert && mr->hide_vert[ml->v]; copy_v3_v3(vert->pos, mv->co); vert->nor = data->normals[ml->v].low; /* Flag for paint mode overlay. */ - if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || + if (poly_hidden || vert_hidden || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { vert->nor.w = -1; @@ -432,18 +434,21 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_PosNorHQ_Data *data = static_cast<MeshExtract_PosNorHQ_Data *>(_data); + const bool poly_hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; + const bool vert_hidden = mr->hide_vert && mr->hide_vert[ml->v]; PosNorHQLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); copy_v3_v3_short(vert->nor, data->normals[ml->v].high); /* Flag for paint mode overlay. */ - if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || + if (poly_hidden || vert_hidden || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { vert->nor[3] = -1; diff --git a/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl new file mode 100644 index 00000000000..7a79f957462 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl @@ -0,0 +1,216 @@ + +/** + * Debugging drawing library + * + * Quick way to draw debug geometry. All input should be in world space and + * will be rendered in the default view. No additional setup required. + **/ + +/** Global switch option. */ +bool drw_debug_draw_enable = true; +const vec4 drw_debug_default_color = vec4(1.0, 0.0, 0.0, 1.0); + +/* -------------------------------------------------------------------- */ +/** \name Interals + * \{ */ + +uint drw_debug_start_draw(uint v_needed) +{ + uint vertid = atomicAdd(drw_debug_draw_v_count, v_needed); + /* NOTE: Skip the header manually. */ + vertid += 1; + return vertid; +} + +uint drw_debug_color_pack(vec4 color) +{ + color = clamp(color, 0.0, 1.0); + uint result = 0; + result |= uint(color.x * 255.0) << 0u; + result |= uint(color.y * 255.0) << 8u; + result |= uint(color.z * 255.0) << 16u; + result |= uint(color.w * 255.0) << 24u; + return result; +} + +void drw_debug_line(inout uint vertid, vec3 v1, vec3 v2, uint color) +{ + drw_debug_verts_buf[vertid++] = DRWDebugVert( + floatBitsToUint(v1.x), floatBitsToUint(v1.y), floatBitsToUint(v1.z), color); + drw_debug_verts_buf[vertid++] = DRWDebugVert( + floatBitsToUint(v2.x), floatBitsToUint(v2.y), floatBitsToUint(v2.z), color); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name API + * \{ */ + +/** + * Draw a line. + */ +void drw_debug_line(vec3 v1, vec3 v2, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const uint v_needed = 2; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + drw_debug_line(vertid, v1, v2, drw_debug_color_pack(color)); + } +} +void drw_debug_line(vec3 v1, vec3 v2) +{ + drw_debug_line(v1, v2, drw_debug_default_color); +} + +/** + * Draw a quad contour. + */ +void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const uint v_needed = 8; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + drw_debug_line(vertid, v1, v2, pcolor); + drw_debug_line(vertid, v2, v3, pcolor); + drw_debug_line(vertid, v3, v4, pcolor); + drw_debug_line(vertid, v4, v1, pcolor); + } +} +void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4) +{ + drw_debug_quad(v1, v2, v3, v4, drw_debug_default_color); +} + +/** + * Draw a point as octahedron wireframe. + */ +void drw_debug_point(vec3 p, float radius, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + vec3 c = vec3(radius, -radius, 0); + vec3 v1 = p + c.xzz; + vec3 v2 = p + c.zxz; + vec3 v3 = p + c.yzz; + vec3 v4 = p + c.zyz; + vec3 v5 = p + c.zzx; + vec3 v6 = p + c.zzy; + + const uint v_needed = 12 * 2; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + drw_debug_line(vertid, v1, v2, pcolor); + drw_debug_line(vertid, v2, v3, pcolor); + drw_debug_line(vertid, v3, v4, pcolor); + drw_debug_line(vertid, v4, v1, pcolor); + drw_debug_line(vertid, v1, v5, pcolor); + drw_debug_line(vertid, v2, v5, pcolor); + drw_debug_line(vertid, v3, v5, pcolor); + drw_debug_line(vertid, v4, v5, pcolor); + drw_debug_line(vertid, v1, v6, pcolor); + drw_debug_line(vertid, v2, v6, pcolor); + drw_debug_line(vertid, v3, v6, pcolor); + drw_debug_line(vertid, v4, v6, pcolor); + } +} +void drw_debug_point(vec3 p, float radius) +{ + drw_debug_point(p, radius, drw_debug_default_color); +} +void drw_debug_point(vec3 p) +{ + drw_debug_point(p, 0.01); +} + +/** + * Draw a sphere wireframe as 3 axes circle. + */ +void drw_debug_sphere(vec3 p, float radius, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const int circle_resolution = 16; + const uint v_needed = circle_resolution * 2 * 3; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + for (int axis = 0; axis < 3; axis++) { + for (int edge = 0; edge < circle_resolution; edge++) { + float angle1 = (2.0 * 3.141592) * float(edge + 0) / float(circle_resolution); + vec3 p1 = vec3(cos(angle1), sin(angle1), 0.0); + p1 = vec3(p1[(0 + axis) % 3], p1[(1 + axis) % 3], p1[(2 + axis) % 3]); + + float angle2 = (2.0 * 3.141592) * float(edge + 1) / float(circle_resolution); + vec3 p2 = vec3(cos(angle2), sin(angle2), 0.0); + p2 = vec3(p2[(0 + axis) % 3], p2[(1 + axis) % 3], p2[(2 + axis) % 3]); + + drw_debug_line(vertid, p1, p2, pcolor); + } + } + } +} +void drw_debug_sphere(vec3 p, float radius) +{ + drw_debug_sphere(p, radius, drw_debug_default_color); +} + +/** + * Draw a matrix transformation as 3 colored axes. + */ +void drw_debug_matrix(mat4 mat, vec4 color) +{ + vec4 p[4] = vec4[4](vec4(0, 0, 0, 1), vec4(1, 0, 0, 1), vec4(0, 1, 0, 1), vec4(0, 0, 1, 1)); + for (int i = 0; i < 4; i++) { + p[i] = mat * p[i]; + p[i].xyz /= p[i].w; + } + drw_debug_line(p[0].xyz, p[0].xyz, vec4(1, 0, 0, 1)); + drw_debug_line(p[0].xyz, p[1].xyz, vec4(0, 1, 0, 1)); + drw_debug_line(p[0].xyz, p[2].xyz, vec4(0, 0, 1, 1)); +} +void drw_debug_matrix(mat4 mat) +{ + drw_debug_matrix(mat, drw_debug_default_color); +} + +/** + * Draw a matrix as a 2 units length bounding box, centered on origin. + */ +void drw_debug_matrix_as_bbox(mat4 mat, vec4 color) +{ + vec4 p[8] = vec4[8](vec4(-1, -1, -1, 1), + vec4(1, -1, -1, 1), + vec4(1, 1, -1, 1), + vec4(-1, 1, -1, 1), + vec4(-1, -1, 1, 1), + vec4(1, -1, 1, 1), + vec4(1, 1, 1, 1), + vec4(-1, 1, 1, 1)); + for (int i = 0; i < 8; i++) { + p[i] = mat * p[i]; + p[i].xyz /= p[i].w; + } + drw_debug_quad(p[0].xyz, p[1].xyz, p[2].xyz, p[3].xyz, color); + drw_debug_line(p[0].xyz, p[4].xyz, color); + drw_debug_line(p[1].xyz, p[5].xyz, color); + drw_debug_line(p[2].xyz, p[6].xyz, color); + drw_debug_line(p[3].xyz, p[7].xyz, color); + drw_debug_quad(p[4].xyz, p[5].xyz, p[6].xyz, p[7].xyz, color); +} +void drw_debug_matrix_as_bbox(mat4 mat) +{ + drw_debug_matrix_as_bbox(mat, drw_debug_default_color); +} + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl new file mode 100644 index 00000000000..0c7f32bd00d --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl @@ -0,0 +1,389 @@ + +/** + * Debug print implementation for shaders. + * + * `print()`: + * Log variable or strings inside the viewport. + * Using a unique non string argument will print the variable name with it. + * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`. + * `drw_print_no_endl()`: + * Same as `print()` but does not finish the line. + * `drw_print_value()`: + * Display only the value of a variable. Does not finish the line. + * `drw_print_value_hex()`: + * Display only the hex representation of a variable. Does not finish the line. + * `drw_print_value_binary()`: Display only the binary representation of a + * variable. Does not finish the line. + * + * IMPORTANT: As it is now, it is not yet thread safe. Only print from one thread. You can use the + * IS_DEBUG_MOUSE_FRAGMENT macro in fragment shader to filter using mouse position or + * IS_FIRST_INVOCATION in compute shaders. + * + * NOTE: Floating point representation might not be very precise (see drw_print_value(float)). + * + * IMPORTANT: Multipler drawcalls can write to the buffer in sequence (if they are from different + * shgroups). However, we add barriers to support this case and it might change the application + * behavior. Uncomment DISABLE_DEBUG_SHADER_drw_print_BARRIER to remove the barriers if that + * happens. But then you are limited to a single invocation output. + * + * IMPORTANT: All of these are copied to the CPU debug libs (draw_debug.cc). They need to be kept + * in sync to write the same data. + */ + +/** Global switch option when you want to silence all prints from all shaders at once. */ +bool drw_debug_print_enable = true; + +/* Set drw_print_col to max value so we will start by creating a new line and get the correct + * threadsafe row. */ +uint drw_print_col = DRW_DEBUG_PRINT_WORD_WRAP_COLUMN; +uint drw_print_row = 0u; + +void drw_print_newline() +{ + if (!drw_debug_print_enable) { + return; + } + drw_print_col = 0u; + drw_print_row = atomicAdd(drw_debug_print_row_shared, 1u) + 1u; +} + +void drw_print_string_start(uint len) +{ + if (!drw_debug_print_enable) { + return; + } + /* Break before word. */ + if (drw_print_col + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + drw_print_newline(); + } +} + +void drw_print_char4(uint data) +{ + if (!drw_debug_print_enable) { + return; + } + /* Convert into char stream. */ + for (; data != 0u; data >>= 8u) { + uint char1 = data & 0xFFu; + /* Check for null terminator. */ + if (char1 == 0x00) { + break; + } + uint cursor = atomicAdd(drw_debug_print_cursor, 1u); + /* NOTE: Skip the header manually. */ + cursor += 4; + if (cursor < DRW_DEBUG_PRINT_MAX) { + /* For future usage. (i.e: Color) */ + uint flags = 0u; + uint col = drw_print_col++; + uint drw_print_header = (flags << 24u) | (drw_print_row << 16u) | (col << 8u); + drw_debug_print_buf[cursor] = drw_print_header | char1; + /* Break word. */ + if (drw_print_col > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + drw_print_newline(); + } + } + } +} + +/** + * NOTE(fclem): Strange behavior emerge when trying to increment the digit + * counter inside the append function. It looks like the compiler does not see + * it is referenced as an index for char4 and thus do not capture the right + * reference. I do not know if this is undefined behavior. As a matter of + * precaution, we implement all the append function separately. This behavior + * was observed on both Mesa & amdgpu-pro. + */ +/* Using ascii char code. Expect char1 to be less or equal to 0xFF. Appends chars to the right. */ +void drw_print_append_char(uint char1, inout uint char4) +{ + char4 = (char4 << 8u) | char1; +} + +void drw_print_append_digit(uint digit, inout uint char4) +{ + const uint char_A = 0x41u; + const uint char_0 = 0x30u; + bool is_hexadecimal = digit > 9u; + char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit)); +} + +void drw_print_append_space(inout uint char4) +{ + char4 = (char4 << 8u) | 0x20u; +} + +void drw_print_value_binary(uint value) +{ + drw_print_no_endl("0b"); + drw_print_string_start(10u * 4u); + uint digits[10] = uint[10](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u); + uint digit = 0u; + for (uint i = 0u; i < 32u; i++) { + drw_print_append_digit(((value >> i) & 1u), digits[digit / 4u]); + digit++; + if ((i % 4u) == 3u) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 9; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value_binary(int value) +{ + drw_print_value_binary(uint(value)); +} + +void drw_print_value_binary(float value) +{ + drw_print_value_binary(floatBitsToUint(value)); +} + +void drw_print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned) +{ + drw_print_string_start(3u * 4u); + const uint blank_value = hex ? 0x30303030u : 0x20202020u; + const uint prefix = hex ? 0x78302020u : 0x20202020u; + uint digits[3] = uint[3](blank_value, blank_value, prefix); + const uint base = hex ? 16u : 10u; + uint digit = 0u; + /* Add `u` suffix. */ + if (is_unsigned) { + drw_print_append_char('u', digits[digit / 4u]); + digit++; + } + /* Number's digits. */ + for (; value != 0u || digit == uint(is_unsigned); value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Add negative sign. */ + if (is_negative) { + drw_print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value_hex(uint value) +{ + drw_print_value_uint(value, true, false, false); +} + +void drw_print_value_hex(int value) +{ + drw_print_value_uint(uint(value), true, false, false); +} + +void drw_print_value_hex(float value) +{ + drw_print_value_uint(floatBitsToUint(value), true, false, false); +} + +void drw_print_value(uint value) +{ + drw_print_value_uint(value, false, false, true); +} + +void drw_print_value(int value) +{ + drw_print_value_uint(uint(abs(value)), false, (value < 0), false); +} + +void drw_print_value(bool value) +{ + if (value) { + drw_print_no_endl("true "); + } + else { + drw_print_no_endl("false"); + } +} + +/* NOTE(@fclem): This is homebrew and might not be 100% accurate (accuracy has + * not been tested and might dependent on compiler implementation). If unsure, + * use drw_print_value_hex and transcribe the value manually with another tool. */ +void drw_print_value(float val) +{ + /* We pad the string to match normal float values length. */ + if (isnan(val)) { + drw_print_no_endl(" NaN"); + return; + } + if (isinf(val)) { + if (sign(val) < 0.0) { + drw_print_no_endl(" -Inf"); + } + else { + drw_print_no_endl(" Inf"); + } + return; + } + + /* Adjusted for significant digits (6) with sign (1), decimal separator (1) + * and exponent (4). */ + const float significant_digits = 6.0; + drw_print_string_start(3u * 4u); + uint digits[3] = uint[3](0x20202020u, 0x20202020u, 0x20202020u); + + float exponent = floor(log(abs(val)) / log(10.0)); + bool display_exponent = exponent >= (significant_digits) || + exponent <= (-significant_digits + 1.0); + + float int_significant_digits = min(exponent + 1.0, significant_digits); + float dec_significant_digits = max(0.0, significant_digits - int_significant_digits); + /* Power to get to the rounding point. */ + float rounding_power = dec_significant_digits; + + if (val == 0.0 || isinf(exponent)) { + display_exponent = false; + int_significant_digits = dec_significant_digits = 1.0; + } + /* Remap to keep significant numbers count. */ + if (display_exponent) { + int_significant_digits = 1.0; + dec_significant_digits = significant_digits - int_significant_digits; + rounding_power = -exponent + dec_significant_digits; + } + /* Round at the last significant digit. */ + val = round(val * pow(10.0, rounding_power)); + /* Get back to final exponent. */ + val *= pow(10.0, -dec_significant_digits); + + float int_part; + float dec_part = modf(val, int_part); + + dec_part *= pow(10.0, dec_significant_digits); + + const uint base = 10u; + uint digit = 0u; + /* Exponent */ + uint value = uint(abs(exponent)); + if (display_exponent) { + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Exponent sign. */ + uint sign_char = (exponent < 0.0) ? '-' : '+'; + drw_print_append_char(sign_char, digits[digit / 4u]); + digit++; + /* Exponent `e` suffix. */ + drw_print_append_char(0x65u, digits[digit / 4u]); + digit++; + } + /* Decimal part. */ + value = uint(abs(dec_part)); +#if 0 /* We don't do that because it makes unstable values really hard to \ + read. */ + /* Trim trailing zeros. */ + while ((value % base) == 0u) { + value /= base; + if (value == 0u) { + break; + } + } +#endif + if (value != 0u) { + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Point separator. */ + drw_print_append_char('.', digits[digit / 4u]); + digit++; + } + /* Integer part. */ + value = uint(abs(int_part)); + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Negative sign. */ + if (val < 0.0) { + drw_print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value(vec2 value) +{ + drw_print_no_endl("vec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(vec3 value) +{ + drw_print_no_endl("vec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(vec4 value) +{ + drw_print_no_endl("vec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(ivec2 value) +{ + drw_print_no_endl("ivec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(ivec3 value) +{ + drw_print_no_endl("ivec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(ivec4 value) +{ + drw_print_no_endl("ivec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(uvec2 value) +{ + drw_print_no_endl("uvec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(uvec3 value) +{ + drw_print_no_endl("uvec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(uvec4 value) +{ + drw_print_no_endl("uvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(bvec2 value) +{ + drw_print_no_endl("bvec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(bvec3 value) +{ + drw_print_no_endl("bvec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(bvec4 value) +{ + drw_print_no_endl("bvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 8eecaa46b58..8ab2ef10e4c 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -37,6 +37,9 @@ layout(std140) uniform viewBlock # endif #endif +#define IS_DEBUG_MOUSE_FRAGMENT (ivec2(gl_FragCoord) == drw_view.mouse_pixel) +#define IS_FIRST_INVOCATION (gl_GlobalInvocationID == uvec3(0)) + #define ViewNear (ViewVecs[0].w) #define ViewFar (ViewVecs[1].w) diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl new file mode 100644 index 00000000000..3fc5294b024 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl @@ -0,0 +1,9 @@ + +/** + * Display debug edge list. + **/ + +void main() +{ + out_color = interp.color; +} diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl new file mode 100644 index 00000000000..92c546aa203 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl @@ -0,0 +1,15 @@ + +/** + * Display debug edge list. + **/ + +void main() +{ + /* Skip the first vertex containing header data. */ + DRWDebugVert vert = drw_debug_verts_buf[gl_VertexID + 1]; + vec3 pos = uintBitsToFloat(uvec3(vert.pos0, vert.pos1, vert.pos2)); + vec4 col = vec4((uvec4(vert.color) >> uvec4(0, 8, 16, 24)) & 0xFFu); + + interp.color = col; + gl_Position = persmat * vec4(pos, 1.0); +} diff --git a/source/blender/draw/intern/shaders/draw_debug_info.hh b/source/blender/draw/intern/shaders/draw_debug_info.hh new file mode 100644 index 00000000000..893a5e537d9 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_info.hh @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Debug print + * + * Allows print() function to have logging support inside shaders. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_debug_print) + .typedef_source("draw_shader_shared.h") + .storage_buf(7, Qualifier::READ_WRITE, "uint", "drw_debug_print_buf[]"); + +GPU_SHADER_INTERFACE_INFO(draw_debug_print_display_iface, "").flat(Type::UINT, "char_index"); + +GPU_SHADER_CREATE_INFO(draw_debug_print_display) + .do_static_compilation(true) + .typedef_source("draw_shader_shared.h") + .storage_buf(7, Qualifier::READ, "uint", "drw_debug_print_buf[]") + .vertex_out(draw_debug_print_display_iface) + .fragment_out(0, Type::VEC4, "out_color") + .vertex_source("draw_debug_print_display_vert.glsl") + .fragment_source("draw_debug_print_display_frag.glsl") + .additional_info("draw_view"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug draw shapes + * + * Allows to draw lines and points just like the DRW_debug module functions. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_debug_draw) + .typedef_source("draw_shader_shared.h") + .storage_buf(6, Qualifier::READ_WRITE, "DRWDebugVert", "drw_debug_verts_buf[]"); + +GPU_SHADER_INTERFACE_INFO(draw_debug_draw_display_iface, "interp").flat(Type::VEC4, "color"); + +GPU_SHADER_CREATE_INFO(draw_debug_draw_display) + .do_static_compilation(true) + .typedef_source("draw_shader_shared.h") + .storage_buf(6, Qualifier::READ, "DRWDebugVert", "drw_debug_verts_buf[]") + .vertex_out(draw_debug_draw_display_iface) + .fragment_out(0, Type::VEC4, "out_color") + .push_constant(Type::MAT4, "persmat") + .vertex_source("draw_debug_draw_display_vert.glsl") + .fragment_source("draw_debug_draw_display_frag.glsl") + .additional_info("draw_view"); + +/** \} */ diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl new file mode 100644 index 00000000000..fe608816109 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl @@ -0,0 +1,133 @@ + +/** + * Display characters using an ascii table. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +bool char_intersect(uvec2 bitmap_position) +{ + /* Using 8x8 = 64bits = uvec2. */ + uvec2 ascii_bitmap[96] = uvec2[96](uvec2(0x00000000u, 0x00000000u), + uvec2(0x18001800u, 0x183c3c18u), + uvec2(0x00000000u, 0x36360000u), + uvec2(0x7f363600u, 0x36367f36u), + uvec2(0x301f0c00u, 0x0c3e031eu), + uvec2(0x0c666300u, 0x00633318u), + uvec2(0x3b336e00u, 0x1c361c6eu), + uvec2(0x00000000u, 0x06060300u), + uvec2(0x060c1800u, 0x180c0606u), + uvec2(0x180c0600u, 0x060c1818u), + uvec2(0x3c660000u, 0x00663cffu), + uvec2(0x0c0c0000u, 0x000c0c3fu), + uvec2(0x000c0c06u, 0x00000000u), + uvec2(0x00000000u, 0x0000003fu), + uvec2(0x000c0c00u, 0x00000000u), + uvec2(0x06030100u, 0x6030180cu), + uvec2(0x6f673e00u, 0x3e63737bu), + uvec2(0x0c0c3f00u, 0x0c0e0c0cu), + uvec2(0x06333f00u, 0x1e33301cu), + uvec2(0x30331e00u, 0x1e33301cu), + uvec2(0x7f307800u, 0x383c3633u), + uvec2(0x30331e00u, 0x3f031f30u), + uvec2(0x33331e00u, 0x1c06031fu), + uvec2(0x0c0c0c00u, 0x3f333018u), + uvec2(0x33331e00u, 0x1e33331eu), + uvec2(0x30180e00u, 0x1e33333eu), + uvec2(0x000c0c00u, 0x000c0c00u), + uvec2(0x000c0c06u, 0x000c0c00u), + uvec2(0x060c1800u, 0x180c0603u), + uvec2(0x003f0000u, 0x00003f00u), + uvec2(0x180c0600u, 0x060c1830u), + uvec2(0x0c000c00u, 0x1e333018u), + uvec2(0x7b031e00u, 0x3e637b7bu), + uvec2(0x3f333300u, 0x0c1e3333u), + uvec2(0x66663f00u, 0x3f66663eu), + uvec2(0x03663c00u, 0x3c660303u), + uvec2(0x66361f00u, 0x1f366666u), + uvec2(0x16467f00u, 0x7f46161eu), + uvec2(0x16060f00u, 0x7f46161eu), + uvec2(0x73667c00u, 0x3c660303u), + uvec2(0x33333300u, 0x3333333fu), + uvec2(0x0c0c1e00u, 0x1e0c0c0cu), + uvec2(0x33331e00u, 0x78303030u), + uvec2(0x36666700u, 0x6766361eu), + uvec2(0x46667f00u, 0x0f060606u), + uvec2(0x6b636300u, 0x63777f7fu), + uvec2(0x73636300u, 0x63676f7bu), + uvec2(0x63361c00u, 0x1c366363u), + uvec2(0x06060f00u, 0x3f66663eu), + uvec2(0x3b1e3800u, 0x1e333333u), + uvec2(0x36666700u, 0x3f66663eu), + uvec2(0x38331e00u, 0x1e33070eu), + uvec2(0x0c0c1e00u, 0x3f2d0c0cu), + uvec2(0x33333f00u, 0x33333333u), + uvec2(0x331e0c00u, 0x33333333u), + uvec2(0x7f776300u, 0x6363636bu), + uvec2(0x1c366300u, 0x6363361cu), + uvec2(0x0c0c1e00u, 0x3333331eu), + uvec2(0x4c667f00u, 0x7f633118u), + uvec2(0x06061e00u, 0x1e060606u), + uvec2(0x30604000u, 0x03060c18u), + uvec2(0x18181e00u, 0x1e181818u), + uvec2(0x00000000u, 0x081c3663u), + uvec2(0x000000ffu, 0x00000000u), + uvec2(0x00000000u, 0x0c0c1800u), + uvec2(0x3e336e00u, 0x00001e30u), + uvec2(0x66663b00u, 0x0706063eu), + uvec2(0x03331e00u, 0x00001e33u), + uvec2(0x33336e00u, 0x3830303eu), + uvec2(0x3f031e00u, 0x00001e33u), + uvec2(0x06060f00u, 0x1c36060fu), + uvec2(0x333e301fu, 0x00006e33u), + uvec2(0x66666700u, 0x0706366eu), + uvec2(0x0c0c1e00u, 0x0c000e0cu), + uvec2(0x3033331eu, 0x30003030u), + uvec2(0x1e366700u, 0x07066636u), + uvec2(0x0c0c1e00u, 0x0e0c0c0cu), + uvec2(0x7f6b6300u, 0x0000337fu), + uvec2(0x33333300u, 0x00001f33u), + uvec2(0x33331e00u, 0x00001e33u), + uvec2(0x663e060fu, 0x00003b66u), + uvec2(0x333e3078u, 0x00006e33u), + uvec2(0x66060f00u, 0x00003b6eu), + uvec2(0x1e301f00u, 0x00003e03u), + uvec2(0x0c2c1800u, 0x080c3e0cu), + uvec2(0x33336e00u, 0x00003333u), + uvec2(0x331e0c00u, 0x00003333u), + uvec2(0x7f7f3600u, 0x0000636bu), + uvec2(0x1c366300u, 0x00006336u), + uvec2(0x333e301fu, 0x00003333u), + uvec2(0x0c263f00u, 0x00003f19u), + uvec2(0x0c0c3800u, 0x380c0c07u), + uvec2(0x18181800u, 0x18181800u), + uvec2(0x0c0c0700u, 0x070c0c38u), + uvec2(0x00000000u, 0x6e3b0000u), + uvec2(0x00000000u, 0x00000000u)); + + if (!in_range_inclusive(bitmap_position, uvec2(0), uvec2(7))) { + return false; + } + uint char_bits = ascii_bitmap[char_index][bitmap_position.y >> 2u & 1u]; + char_bits = (char_bits >> ((bitmap_position.y & 3u) * 8u + bitmap_position.x)); + return (char_bits & 1u) != 0u; +} + +void main() +{ + uvec2 bitmap_position = uvec2(gl_PointCoord.xy * 8.0); + /* Point coord start from top left corner. But layout is from bottom to top. */ + bitmap_position.y = 7 - bitmap_position.y; + + if (char_intersect(bitmap_position)) { + out_color = vec4(1); + } + else if (char_intersect(bitmap_position + uvec2(0, 1))) { + /* Shadow */ + out_color = vec4(0, 0, 0, 1); + } + else { + /* Transparent Background for ease of read. */ + out_color = vec4(0, 0, 0, 0.2); + } +}
\ No newline at end of file diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl new file mode 100644 index 00000000000..c8fc3815436 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl @@ -0,0 +1,29 @@ + +/** + * Display characters using an ascii table. Outputs one point per character. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + /* Skip first 4 chars containing header data. */ + uint char_data = drw_debug_print_buf[gl_VertexID + 4]; + char_index = (char_data & 0xFFu) - 0x20u; + + /* Discard invalid chars. */ + if (char_index >= 96u) { + gl_Position = vec4(-1); + gl_PointSize = 0.0; + return; + } + uint row = (char_data >> 16u) & 0xFFu; + uint col = (char_data >> 8u) & 0xFFu; + + float char_size = 16.0; + /* Change anchor point to the top left. */ + vec2 pos_on_screen = char_size * vec2(col, row) + char_size * 4; + gl_Position = vec4( + pos_on_screen * drw_view.viewport_size_inverse * vec2(2.0, -2.0) - vec2(1.0, -1.0), 0, 1); + gl_PointSize = char_size; +}
\ No newline at end of file diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 08d5d6558e0..ae15bc39ec8 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -59,7 +59,7 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases, const uint hit_object = select_id & 0xFFFF; Base *base = NULL; EditBone *ebone = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ + /* TODO(@campbellbarton): optimize, eg: sort & binary search. */ for (uint base_index = 0; base_index < bases_len; base_index++) { if (bases[base_index]->object->runtime.select_id == hit_object) { base = bases[base_index]; @@ -83,7 +83,7 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects, const uint hit_object = select_id & 0xFFFF; Object *ob = NULL; EditBone *ebone = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ + /* TODO(@campbellbarton): optimize, eg: sort & binary search. */ for (uint ob_index = 0; ob_index < objects_len; ob_index++) { if (objects[ob_index]->runtime.select_id == hit_object) { ob = objects[ob_index]; @@ -107,7 +107,7 @@ Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases, const uint hit_object = select_id & 0xFFFF; Base *base = NULL; bPoseChannel *pchan = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ + /* TODO(@campbellbarton): optimize, eg: sort & binary search. */ for (uint base_index = 0; base_index < bases_len; base_index++) { if (bases[base_index]->object->runtime.select_id == hit_object) { base = bases[base_index]; @@ -1452,7 +1452,7 @@ static void armature_select_more_less(Object *ob, bool more) bArmature *arm = (bArmature *)ob->data; EditBone *ebone; - /* XXX(campbell): eventually we shouldn't need this. */ + /* XXX(@campbellbarton): eventually we shouldn't need this. */ ED_armature_edit_sync_selection(arm->edbo); /* count bones & store selection state */ diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index b6b5d3ee495..6756dec1c95 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -166,7 +166,7 @@ bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer, /* Since we do unified select, we don't shift+select a bone if the * armature object was not active yet. - * NOTE(campbell): special exception for armature mode so we can do multi-select + * NOTE(@campbellbarton): special exception for armature mode so we can do multi-select * we could check for multi-select explicitly but think its fine to * always give predictable behavior in weight paint mode. */ if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) { diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 852bfb00ea6..164336c4b22 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1279,7 +1279,7 @@ void ED_curve_editnurb_make(Object *obedit) if (actkey) { // XXX strcpy(G.editModeTitleExtra, "(Key) "); - /* TODO(campbell): undo_system: investigate why this was needed. */ + /* TODO(@campbellbarton): undo_system: investigate why this was needed. */ #if 0 undo_editmode_clear(); #endif @@ -1975,7 +1975,7 @@ static int sel_to_copy_ints(const BPoint *bp, else if (not_full == -1) { not_full = selected_in_curr_leg; } - /* We have partialy selected leg in opposite dimension if condition is met. */ + /* We have partially selected leg in opposite dimension if condition is met. */ else if (not_full != selected_in_curr_leg) { return -1; } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index befff611d58..70f12151fdd 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1107,7 +1107,7 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive /* Initialize mouse points. */ static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event) { - copy_v2fl_v2i(tgpi->mval, event->mval); + WM_event_drag_start_mval_fl(event, tgpi->region, tgpi->mval); copy_v2_v2(tgpi->origin, tgpi->mval); copy_v2_v2(tgpi->start, tgpi->mval); copy_v2_v2(tgpi->end, tgpi->mval); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 9d3fd5af47f..71ddffca8a9 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -390,7 +390,10 @@ void ED_keymap_mesh(struct wmKeyConfig *keyconf); * Copy the face flags, most importantly selection from the mesh to the final derived mesh, * use in object mode when selecting faces (while painting). */ -void paintface_flush_flags(struct bContext *C, struct Object *ob, short flag); +void paintface_flush_flags(struct bContext *C, + struct Object *ob, + bool flush_selection, + bool flush_hidden); /** * \return True when pick finds an element or the selection changed. */ diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index db44d9af706..f9ca578f282 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -57,13 +57,13 @@ struct SnapObjectParams { /* Geometry for snapping in edit mode. */ eSnapEditType edit_mode_type; /* snap to the closest element, use when using more than one snap type */ - bool use_occlusion_test : true; + bool use_occlusion_test : 1; /* exclude back facing geometry from snapping */ - bool use_backface_culling : true; + bool use_backface_culling : 1; /* Break nearest face snapping into steps to improve transformations across U-shaped targets. */ short face_nearest_steps; /* Enable to force nearest face snapping to snap to target the source was initially near. */ - bool keep_on_same_target; + bool keep_on_same_target : 1; }; typedef struct SnapObjectContext SnapObjectContext; diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 931bb7be8bf..bb95ea97c1c 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -1171,7 +1171,7 @@ void ED_view3d_camera_lock_init(const struct Depsgraph *depsgraph, * * Apply the 3D Viewport transformation back to the camera object. * - * \return true if the camera is moved. + * \return true if the camera (or one of it's parents) was moved. */ bool ED_view3d_camera_lock_sync(const struct Depsgraph *depsgraph, struct View3D *v3d, @@ -1197,14 +1197,22 @@ bool ED_view3d_camera_lock_autokey(struct View3D *v3d, void ED_view3d_lock_clear(struct View3D *v3d); /** + * Check if creating an undo step should be performed if the viewport moves. + * \return true if #ED_view3d_camera_lock_undo_push would do an undo push. + */ +bool ED_view3d_camera_lock_undo_test(const View3D *v3d, + const RegionView3D *rv3d, + struct bContext *C); + +/** * Create an undo step when the camera is locked to the view. * \param str: The name of the undo step (typically #wmOperatorType.name should be used). * * \return true when the call to push an undo step was made. */ bool ED_view3d_camera_lock_undo_push(const char *str, - View3D *v3d, - struct RegionView3D *rv3d, + const View3D *v3d, + const struct RegionView3D *rv3d, struct bContext *C); /** @@ -1214,8 +1222,8 @@ bool ED_view3d_camera_lock_undo_push(const char *str, * where adding a separate undo step each time isn't desirable. */ bool ED_view3d_camera_lock_undo_grouped_push(const char *str, - View3D *v3d, - struct RegionView3D *rv3d, + const View3D *v3d, + const struct RegionView3D *rv3d, struct bContext *C); #define VIEW3D_MARGIN 1.4f diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index c54bcad905f..e4a973a375e 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -27,13 +27,13 @@ set(INC ) set(SRC - eyedroppers/interface_eyedropper.c eyedroppers/eyedropper_color.c eyedroppers/eyedropper_colorband.c eyedroppers/eyedropper_datablock.c eyedroppers/eyedropper_depth.c eyedroppers/eyedropper_driver.c eyedroppers/eyedropper_gpencil_color.c + eyedroppers/interface_eyedropper.c interface.cc interface_align.c interface_anim.cc diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 2f9e69137ed..c076845af3c 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -1310,7 +1310,7 @@ static bool ui_but_event_operator_string_from_panel(const bContext *C, IDP_AddToGroup(prop_panel, IDP_New(IDP_INT, ®ion_type_val, "region_type")); for (int i = 0; i < 2; i++) { - /* FIXME(campbell): We can't reasonably search all configurations - long term. */ + /* FIXME(@campbellbarton): We can't reasonably search all configurations - long term. */ IDPropertyTemplate val = {0}; val.i = i; diff --git a/source/blender/editors/interface/interface_anim.cc b/source/blender/editors/interface/interface_anim.cc index 8e898b7fe66..4da6cefd8de 100644 --- a/source/blender/editors/interface/interface_anim.cc +++ b/source/blender/editors/interface/interface_anim.cc @@ -325,7 +325,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy) return; } - /* FIXME(campbell), swapping active pointer is weak. */ + /* FIXME(@campbellbarton): swapping active pointer is weak. */ SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->but.active); wm->op_undo_depth++; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 80fd0cbe16e..6ee421fb4d2 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4344,15 +4344,18 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but, ARegion *region, const wmEvent *event) { - float xmax = but->rect.xmax; - const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */ - int x = event->xy[0], y = event->xy[1]; + if (BLI_listbase_is_empty(&but->extra_op_icons)) { + return NULL; + } + int x = event->xy[0], y = event->xy[1]; ui_window_to_block(region, but->block, &x, &y); if (!BLI_rctf_isect_pt(&but->rect, x, y)) { return NULL; } + const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */ + float xmax = but->rect.xmax; /* Same as in 'widget_draw_extra_icons', icon padding from the right edge. */ xmax -= 0.2 * icon_size; @@ -8810,7 +8813,7 @@ void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo) { uiBut *activebut = ui_context_rna_button_active(C); if (activebut) { - /* TODO(campbell): look into a better way to handle the button change + /* TODO(@campbellbarton): look into a better way to handle the button change * currently this is mainly so reset defaults works for the * operator redo panel. */ uiBlock *block = activebut->block; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index c19e842aad8..5bb33576723 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1824,7 +1824,7 @@ static void icon_draw_size(float x, } else if (di->type == ICON_TYPE_GEOM) { #ifdef USE_UI_TOOLBAR_HACK - /* TODO(campbell): scale icons up for toolbar, + /* TODO(@campbellbarton): scale icons up for toolbar, * we need a way to detect larger buttons and do this automatic. */ { float scale = (float)ICON_DEFAULT_HEIGHT_TOOLBAR / (float)ICON_DEFAULT_HEIGHT; @@ -1839,7 +1839,7 @@ static void icon_draw_size(float x, const bool geom_inverted = di->data.geom.inverted; /* This could re-generate often if rendered at different sizes in the one interface. - * TODO(campbell): support caching multiple sizes. */ + * TODO(@campbellbarton): support caching multiple sizes. */ ImBuf *ibuf = di->data.geom.image_cache; if ((ibuf == NULL) || (ibuf->x != w) || (ibuf->y != h) || (invert != geom_inverted)) { if (ibuf) { diff --git a/source/blender/editors/interface/interface_region_popover.cc b/source/blender/editors/interface/interface_region_popover.cc index c152a9aacd5..17c8d890755 100644 --- a/source/blender/editors/interface/interface_region_popover.cc +++ b/source/blender/editors/interface/interface_region_popover.cc @@ -397,7 +397,7 @@ void UI_popover_end(bContext *C, uiPopover *pup, wmKeyMap *keymap) pup->window = window; - /* TODO(campbell): we may want to make this configurable. + /* TODO(@campbellbarton): we may want to make this configurable. * The begin/end stype of calling popups doesn't allow 'can_refresh' to be set. * For now close this style of popovers when accessed. */ UI_block_flag_disable(pup->block, UI_BLOCK_KEEP_OPEN); diff --git a/source/blender/editors/interface/interface_region_tooltip.cc b/source/blender/editors/interface/interface_region_tooltip.cc index 6a39b761983..8d88261c328 100644 --- a/source/blender/editors/interface/interface_region_tooltip.cc +++ b/source/blender/editors/interface/interface_region_tooltip.cc @@ -7,7 +7,7 @@ * ToolTip Region and Construction */ -/* TODO(campbell): +/* TODO(@campbellbarton): * We may want to have a higher level API that initializes a timer, * checks for mouse motion and clears the tool-tip afterwards. * We never want multiple tool-tips at once diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index f566299960a..5813f1d090c 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -663,7 +663,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, * system override with reset. */ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY(id)) { if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - BKE_lib_override_library_get(bmain, id, &id); + BKE_lib_override_library_get(bmain, id, NULL, &id); } if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) { id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; @@ -6458,13 +6458,13 @@ bool uiTemplateEventFromKeymapItem(struct uiLayout *layout, for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) { uiItemL(layout, "", icon_mod[j]); } - uiItemL(layout, text, icon); + uiItemL(layout, TIP_(text), icon); ok = true; } else if (text_fallback) { const char *event_text = WM_key_event_string(kmi->type, true); uiItemL(layout, event_text, ICON_NONE); - uiItemL(layout, text, ICON_NONE); + uiItemL(layout, TIP_(text), ICON_NONE); ok = true; } return ok; diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index a716c00d5d9..568ece00c4c 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -11,9 +11,9 @@ set(INC ../../io/collada ../../io/common ../../io/gpencil + ../../io/stl ../../io/usd ../../io/wavefront_obj - ../../io/stl ../../makesdna ../../makesrna ../../windowmanager @@ -33,8 +33,8 @@ set(SRC io_gpencil_utils.c io_obj.c io_ops.c - io_usd.c io_stl_ops.c + io_usd.c io_alembic.h io_cache.h @@ -42,8 +42,8 @@ set(SRC io_gpencil.h io_obj.h io_ops.h - io_usd.h io_stl_ops.h + io_usd.h ) set(LIB diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 662ff601e29..c151baf13ef 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -382,11 +382,6 @@ static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS static int wm_obj_import_exec(bContext *C, wmOperator *op) { - if (!RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_report(op->reports, RPT_ERROR, "No filename given"); - return OPERATOR_CANCELLED; - } - struct OBJImportParams import_params; RNA_string_get(op->ptr, "filepath", import_params.filepath); import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size"); @@ -395,8 +390,35 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) import_params.import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups"); import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); import_params.relative_paths = ((U.flag & USER_RELPATHS) != 0); - - OBJ_import(C, &import_params); + import_params.clear_selection = true; + + int files_len = RNA_collection_length(op->ptr, "files"); + if (files_len) { + /* Importing multiple files: loop over them and import one by one. */ + PointerRNA fileptr; + PropertyRNA *prop; + char dir_only[FILE_MAX], file_only[FILE_MAX]; + + RNA_string_get(op->ptr, "directory", dir_only); + prop = RNA_struct_find_property(op->ptr, "files"); + for (int i = 0; i < files_len; i++) { + RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr); + RNA_string_get(&fileptr, "name", file_only); + BLI_join_dirfile( + import_params.filepath, sizeof(import_params.filepath), dir_only, file_only); + import_params.clear_selection = (i == 0); + OBJ_import(C, &import_params); + } + } + else if (RNA_struct_property_is_set(op->ptr, "filepath")) { + /* Importing one file. */ + RNA_string_get(op->ptr, "filepath", import_params.filepath); + OBJ_import(C, &import_params); + } + else { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } Scene *scene = CTX_data_scene(C); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); @@ -454,7 +476,8 @@ void WM_OT_obj_import(struct wmOperatorType *ot) FILE_TYPE_FOLDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS | + WM_FILESEL_DIRECTORY | WM_FILESEL_FILES, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); RNA_def_float( diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c index 7615a57c8fe..8265225e08a 100644 --- a/source/blender/editors/lattice/editlattice_undo.c +++ b/source/blender/editors/lattice/editlattice_undo.c @@ -46,7 +46,7 @@ static CLG_LogRef LOG = {"ed.undo.lattice"}; /** \name Undo Conversion * \{ */ -/* TODO(Campbell): this could contain an entire 'Lattice' struct. */ +/* TODO(@campbellbarton): this could contain an entire 'Lattice' struct. */ typedef struct UndoLattice { BPoint *def; int pntsu, pntsv, pntsw, actbp; diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc index 3608e162727..0d988b9551f 100644 --- a/source/blender/editors/mesh/editface.cc +++ b/source/blender/editors/mesh/editface.cc @@ -17,6 +17,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "BKE_attribute.hh" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_global.h" @@ -36,14 +37,18 @@ /* own include */ -void paintface_flush_flags(bContext *C, Object *ob, short flag) +void paintface_flush_flags(bContext *C, + Object *ob, + const bool flush_selection, + const bool flush_hidden) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); MPoly *polys, *mp_orig; const int *index_array = nullptr; int totpoly; - BLI_assert((flag & ~(SELECT | ME_HIDE)) == 0); + BLI_assert(flush_selection || flush_hidden); if (me == nullptr) { return; @@ -53,7 +58,7 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) /* we could call this directly in all areas that change selection, * since this could become slow for realtime updates (circle-select for eg) */ - if (flag & SELECT) { + if (flush_selection) { BKE_mesh_flush_select_from_polys(me); } @@ -64,8 +69,11 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) return; } + bke::AttributeAccessor attributes_me = bke::mesh_attributes(*me); Mesh *me_orig = (Mesh *)ob_eval->runtime.data_orig; + bke::MutableAttributeAccessor attributes_orig = bke::mesh_attributes_for_write(*me_orig); Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval; + bke::MutableAttributeAccessor attributes_eval = bke::mesh_attributes_for_write(*me_eval); bool updated = false; if (me_orig != nullptr && me_eval != nullptr && me_orig->totpoly == me->totpoly) { @@ -73,13 +81,17 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) for (int i = 0; i < me->totpoly; i++) { me_orig->mpoly[i].flag = me->mpoly[i].flag; } - - /* If the mesh has only deform modifiers, the evaluated mesh shares arrays. */ - if (me_eval->mpoly == me_orig->mpoly) { - updated = true; + if (flush_hidden) { + const VArray<bool> hide_poly_me = attributes_me.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + bke::SpanAttributeWriter<bool> hide_poly_orig = + attributes_orig.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE); + hide_poly_me.materialize(hide_poly_orig.span); + hide_poly_orig.finish(); } + /* Mesh polys => Final derived polys */ - else if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { + if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { polys = me_eval->mpoly; totpoly = me_eval->totpoly; @@ -91,13 +103,24 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) polys[i].flag = mp_orig->flag; } } + const VArray<bool> hide_poly_orig = attributes_orig.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + bke::SpanAttributeWriter<bool> hide_poly_eval = + attributes_eval.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE); + for (const int i : IndexRange(me_eval->totpoly)) { + const int orig_poly_index = index_array[i]; + if (orig_poly_index != ORIGINDEX_NONE) { + hide_poly_eval.span[i] = hide_poly_orig[orig_poly_index]; + } + } + hide_poly_eval.finish(); updated = true; } } if (updated) { - if (flag & ME_HIDE) { + if (flush_hidden) { BKE_mesh_batch_cache_dirty_tag(me_eval, BKE_MESH_BATCH_DIRTY_ALL); } else { @@ -115,59 +138,79 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) void paintface_hide(bContext *C, Object *ob, const bool unselected) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr || me->totpoly == 0) { return; } + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>( + ".hide_poly", ATTR_DOMAIN_FACE); + for (int i = 0; i < me->totpoly; i++) { MPoly *mpoly = &me->mpoly[i]; - if ((mpoly->flag & ME_HIDE) == 0) { + if (!hide_poly.span[i]) { if (((mpoly->flag & ME_FACE_SEL) == 0) == unselected) { - mpoly->flag |= ME_HIDE; + hide_poly.span[i] = true; } } - if (mpoly->flag & ME_HIDE) { + if (hide_poly.span[i]) { mpoly->flag &= ~ME_FACE_SEL; } } + hide_poly.finish(); + BKE_mesh_flush_hidden_from_polys(me); - paintface_flush_flags(C, ob, SELECT | ME_HIDE); + paintface_flush_flags(C, ob, true, true); } void paintface_reveal(bContext *C, Object *ob, const bool select) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr || me->totpoly == 0) { return; } - for (int i = 0; i < me->totpoly; i++) { - MPoly *mpoly = &me->mpoly[i]; - if (mpoly->flag & ME_HIDE) { - SET_FLAG_FROM_TEST(mpoly->flag, select, ME_FACE_SEL); - mpoly->flag &= ~ME_HIDE; + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + + if (select) { + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; + if (hide_poly[i]) { + mpoly->flag |= ME_FACE_SEL; + } } } + attributes.remove(".hide_poly"); + BKE_mesh_flush_hidden_from_polys(me); - paintface_flush_flags(C, ob, SELECT | ME_HIDE); + paintface_flush_flags(C, ob, true, true); } /* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select) { + using namespace blender; bool do_it = true; bool mark = false; BLI_bitmap *edge_tag = BLI_BITMAP_NEW(me->totedge, __func__); BLI_bitmap *poly_tag = BLI_BITMAP_NEW(me->totpoly, __func__); + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (index != (uint)-1) { /* only put face under cursor in array */ MPoly *mp = &me->mpoly[index]; @@ -178,7 +221,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo /* fill array by selection */ for (int i = 0; i < me->totpoly; i++) { MPoly *mp = &me->mpoly[i]; - if (mp->flag & ME_HIDE) { + if (hide_poly[i]) { /* pass */ } else if (mp->flag & ME_FACE_SEL) { @@ -194,7 +237,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo /* expand selection */ for (int i = 0; i < me->totpoly; i++) { MPoly *mp = &me->mpoly[i]; - if (mp->flag & ME_HIDE) { + if (hide_poly[i]) { continue; } @@ -249,22 +292,27 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b select_linked_tfaces_with_seams(me, index, select); - paintface_flush_flags(C, ob, SELECT); + paintface_flush_flags(C, ob, true, false); } bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr) { return false; } + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (action == SEL_TOGGLE) { action = SEL_SELECT; for (int i = 0; i < me->totpoly; i++) { MPoly *mpoly = &me->mpoly[i]; - if ((mpoly->flag & ME_HIDE) == 0 && mpoly->flag & ME_FACE_SEL) { + if (!hide_poly[i] && mpoly->flag & ME_FACE_SEL) { action = SEL_DESELECT; break; } @@ -275,7 +323,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl for (int i = 0; i < me->totpoly; i++) { MPoly *mpoly = &me->mpoly[i]; - if ((mpoly->flag & ME_HIDE) == 0) { + if (!hide_poly[i]) { switch (action) { case SEL_SELECT: if ((mpoly->flag & ME_FACE_SEL) == 0) { @@ -299,7 +347,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl if (changed) { if (flush_flags) { - paintface_flush_flags(C, ob, SELECT); + paintface_flush_flags(C, ob, true, false); } } return changed; @@ -307,6 +355,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) { + using namespace blender; bool ok = false; float vec[3], bmat[3][3]; @@ -318,9 +367,13 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) copy_m3_m4(bmat, ob->obmat); + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + for (int i = 0; i < me->totpoly; i++) { MPoly *mp = &me->mpoly[i]; - if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) { + if (hide_poly[i] || !(mp->flag & ME_FACE_SEL)) { continue; } @@ -342,6 +395,7 @@ bool paintface_mouse_select(bContext *C, const SelectPick_Params *params, Object *ob) { + using namespace blender; MPoly *mpoly_sel = nullptr; uint index; bool changed = false; @@ -350,10 +404,14 @@ bool paintface_mouse_select(bContext *C, /* Get the face under the cursor */ Mesh *me = BKE_mesh_from_object(ob); + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { if (index < me->totpoly) { mpoly_sel = me->mpoly + index; - if ((mpoly_sel->flag & ME_HIDE) == 0) { + if (!hide_poly[index]) { found = true; } } @@ -402,7 +460,7 @@ bool paintface_mouse_select(bContext *C, /* image window redraw */ - paintface_flush_flags(C, ob, SELECT); + paintface_flush_flags(C, ob, true, false); ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */ changed = true; } @@ -463,17 +521,24 @@ void paintvert_tag_select_update(bContext *C, Object *ob) bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr) { return false; } + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (action == SEL_TOGGLE) { action = SEL_SELECT; for (int i = 0; i < me->totvert; i++) { MVert *mvert = &me->mvert[i]; - if ((mvert->flag & ME_HIDE) == 0 && mvert->flag & SELECT) { + if (!hide_poly[i] && mvert->flag & SELECT) { action = SEL_DESELECT; break; } @@ -483,7 +548,7 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) bool changed = false; for (int i = 0; i < me->totvert; i++) { MVert *mvert = &me->mvert[i]; - if ((mvert->flag & ME_HIDE) == 0) { + if (!hide_vert[i]) { switch (action) { case SEL_SELECT: if ((mvert->flag & SELECT) == 0) { @@ -526,6 +591,7 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr || me->dvert == nullptr) { @@ -536,10 +602,14 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) paintvert_deselect_all_visible(ob, SEL_DESELECT, false); } + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + for (int i = 0; i < me->totvert; i++) { MVert *mv = &me->mvert[i]; MDeformVert *dv = &me->dvert[i]; - if ((mv->flag & ME_HIDE) == 0) { + if (!hide_poly[i]) { if (dv->dw == nullptr) { /* if null weight then not grouped */ mv->flag |= SELECT; @@ -554,25 +624,30 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) void paintvert_hide(bContext *C, Object *ob, const bool unselected) { - Mesh *const me = BKE_mesh_from_object(ob); - - if (me == nullptr || me->totvert == 0) { + using namespace blender; + Mesh *me = BKE_mesh_from_object(ob); + if (me == NULL || me->totvert == 0) { return; } - for (int i = 0; i < me->totvert; i++) { - MVert *const mvert = &me->mvert[i]; + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_span<bool>( + ".hide_vert", ATTR_DOMAIN_POINT); + MutableSpan<MVert> verts(me->mvert, me->totvert); - if ((mvert->flag & ME_HIDE) == 0) { - if (((mvert->flag & SELECT) == 0) == unselected) { - mvert->flag |= ME_HIDE; + for (const int i : verts.index_range()) { + MVert &vert = verts[i]; + if (!hide_vert.span[i]) { + if (((vert.flag & SELECT) == 0) == unselected) { + hide_vert.span[i] = true; } } - if (mvert->flag & ME_HIDE) { - mvert->flag &= ~SELECT; + if (hide_vert.span[i]) { + vert.flag &= ~SELECT; } } + hide_vert.finish(); BKE_mesh_flush_hidden_from_verts(me); @@ -582,21 +657,27 @@ void paintvert_hide(bContext *C, Object *ob, const bool unselected) void paintvert_reveal(bContext *C, Object *ob, const bool select) { - Mesh *const me = BKE_mesh_from_object(ob); - - if (me == nullptr || me->totvert == 0) { + using namespace blender; + Mesh *me = BKE_mesh_from_object(ob); + if (me == NULL || me->totvert == 0) { return; } - for (int i = 0; i < me->totvert; i++) { - MVert *const mvert = &me->mvert[i]; + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + MutableSpan<MVert> verts(me->mvert, me->totvert); - if (mvert->flag & ME_HIDE) { - SET_FLAG_FROM_TEST(mvert->flag, select, SELECT); - mvert->flag &= ~ME_HIDE; + for (const int i : verts.index_range()) { + MVert &vert = verts[i]; + if (hide_vert[i]) { + SET_FLAG_FROM_TEST(vert.flag, select, SELECT); } } + /* Remove the hide attribute to reveal all vertices. */ + attributes.remove(".hide_vert"); + BKE_mesh_flush_hidden_from_verts(me); paintvert_flush_flags(ob); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 5680865ae67..d4c5504615a 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -4300,7 +4300,7 @@ static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, Object *ob) } /** - * A post version is needed to to delay recalculating tessellation after making cuts. + * A post version is needed to delay recalculating tessellation after making cuts. * Without this, knife-project can't use the BVH tree to select geometry after a cut, see: T98349. */ static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object *ob) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index c5add97fb00..7de5ad9f151 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -936,7 +936,7 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) Object *obedit = objects[ob_index]; BMEditMesh *em = BKE_editmesh_from_object(obedit); - if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totvertsel == 0)) { + if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { continue; } diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index d75c92f963f..af8084e16c4 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -594,6 +594,10 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo /* Uncomment for troubleshooting. */ // BM_mesh_validate(em->bm); + /* Copy the ID name characters to the mesh so code that depends on accessing the ID type can work + * on it. Necessary to use the attribute API. */ + strcpy(um->me.id.name, "MEundomesh_from_editmesh"); + BM_mesh_bm_to_me( NULL, em->bm, diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 195a3686b3b..e931dd02a9e 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -593,6 +593,31 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v) return vmap->vert[v]; } +static void bm_uv_ensure_head_table(UvElementMap *element_map) +{ + if (element_map->head_table) { + return; + } + + /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */ + element_map->head_table = MEM_mallocN(sizeof(*element_map->head_table) * element_map->total_uvs, + "uv_element_map_head_table"); + UvElement **head_table = element_map->head_table; + for (int i = 0; i < element_map->total_uvs; i++) { + UvElement *head = element_map->storage + i; + if (head->separate) { + UvElement *element = head; + while (element) { + head_table[element - element_map->storage] = head; + element = element->next; + if (element && element->separate) { + break; + } + } + } + } +} + #define INVALID_ISLAND ((unsigned int)-1) static void bm_uv_assign_island(UvElementMap *element_map, @@ -620,23 +645,9 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, bool uv_selected, int cd_loop_uv_offset) { - int total_uvs = element_map->total_uvs; + bm_uv_ensure_head_table(element_map); - /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */ - UvElement **head_table = MEM_mallocN(sizeof(*head_table) * total_uvs, "uv_island_head_table"); - for (int i = 0; i < total_uvs; i++) { - UvElement *head = element_map->storage + i; - if (head->separate) { - UvElement *element = head; - while (element) { - head_table[element - element_map->storage] = head; - element = element->next; - if (element && element->separate) { - break; - } - } - } - } + int total_uvs = element_map->total_uvs; /* Depth first search the graph, building islands as we go. */ int nislands = 0; @@ -676,7 +687,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, if (!uv_selected || uvedit_edge_select_test(scene, element->l, cd_loop_uv_offset)) { UvElement *next = BM_uv_element_get(element_map, element->l->next->f, element->l->next); if (next->island == INVALID_ISLAND) { - UvElement *tail = head_table[next - element_map->storage]; + UvElement *tail = element_map->head_table[next - element_map->storage]; stack_uv[stacksize_uv++] = tail; while (tail) { bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); @@ -692,7 +703,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, cd_loop_uv_offset)) { UvElement *prev = BM_uv_element_get(element_map, element->l->prev->f, element->l->prev); if (prev->island == INVALID_ISLAND) { - UvElement *tail = head_table[prev - element_map->storage]; + UvElement *tail = element_map->head_table[prev - element_map->storage]; stack_uv[stacksize_uv++] = tail; while (tail) { bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); @@ -716,11 +727,129 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, BLI_assert(islandbufsize == total_uvs); MEM_SAFE_FREE(stack_uv); - MEM_SAFE_FREE(head_table); + MEM_SAFE_FREE(element_map->head_table); return nislands; } +static void bm_uv_build_islands(UvElementMap *element_map, + BMesh *bm, + const Scene *scene, + bool uv_selected) +{ + int totuv = element_map->total_uvs; + int nislands = 0; + int islandbufsize = 0; + + /* map holds the map from current vmap->buf to the new, sorted map */ + uint *map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap"); + BMFace **stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack"); + UvElement *islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer"); + /* Island number for BMFaces. */ + int *island_number = MEM_callocN(sizeof(*island_number) * bm->totface, "uv_island_number_face"); + copy_vn_i(island_number, bm->totface, INVALID_ISLAND); + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ? + scene->toolsettings->selectmode & SCE_SELECT_EDGE : + scene->toolsettings->uv_selectmode & UV_SELECT_EDGE; + if (use_uv_edge_connectivity) { + nislands = bm_uv_edge_select_build_islands( + element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset); + islandbufsize = totuv; + } + + for (int i = 0; i < totuv; i++) { + if (element_map->storage[i].island == INVALID_ISLAND) { + int stacksize = 0; + element_map->storage[i].island = nislands; + stack[0] = element_map->storage[i].l->f; + island_number[BM_elem_index_get(stack[0])] = nislands; + stacksize = 1; + + while (stacksize > 0) { + BMFace *efa = stack[--stacksize]; + + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } + + UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)]; + + for (UvElement *element = initelement; element; element = element->next) { + if (element->separate) { + initelement = element; + } + + if (element->l->f == efa) { + /* found the uv corresponding to our face and vertex. + * Now fill it to the buffer */ + bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++); + + for (element = initelement; element; element = element->next) { + if (element->separate && element != initelement) { + break; + } + + if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) { + stack[stacksize++] = element->l->f; + island_number[BM_elem_index_get(element->l->f)] = nislands; + } + } + break; + } + } + } + } + + nislands++; + } + } + + MEM_SAFE_FREE(island_number); + + /* remap */ + for (int i = 0; i < bm->totvert; i++) { + /* important since we may do selection only. Some of these may be NULL */ + if (element_map->vertex[i]) { + element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]]; + } + } + + element_map->island_indices = MEM_callocN(sizeof(*element_map->island_indices) * nislands, + __func__); + element_map->island_total_uvs = MEM_callocN(sizeof(*element_map->island_total_uvs) * nislands, + __func__); + element_map->island_total_unique_uvs = MEM_callocN( + sizeof(*element_map->island_total_unique_uvs) * nislands, __func__); + int j = 0; + for (int i = 0; i < totuv; i++) { + UvElement *next = element_map->storage[i].next; + islandbuf[map[i]].next = next ? &islandbuf[map[next - element_map->storage]] : NULL; + + if (islandbuf[i].island != j) { + j++; + element_map->island_indices[j] = i; + } + BLI_assert(islandbuf[i].island == j); + element_map->island_total_uvs[j]++; + if (islandbuf[i].separate) { + element_map->island_total_unique_uvs[j]++; + } + } + + MEM_SAFE_FREE(element_map->storage); + element_map->storage = islandbuf; + islandbuf = NULL; + element_map->total_islands = nislands; + MEM_SAFE_FREE(stack); + MEM_SAFE_FREE(map); +} + UvElementMap *BM_uv_element_map_create(BMesh *bm, const Scene *scene, const bool uv_selected, @@ -824,12 +953,13 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, winding[j] = cross_poly_v2(tf_uv, efa->len) > 0; } } + BLI_buffer_free(&tf_uv_buf); /* For each BMVert, sort associated linked list into unique uvs. */ - int i; - BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, i) { + int ev_index; + BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) { UvElement *newvlist = NULL; - UvElement *vlist = element_map->vertex[i]; + UvElement *vlist = element_map->vertex[ev_index]; while (vlist) { /* Detach head from unsorted list. */ @@ -845,21 +975,32 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, UvElement *lastv = NULL; UvElement *iterv = vlist; - /* Scan through unsorted list, finding UvElements which match `v`. */ + /* Scan through unsorted list, finding UvElements which are connected to `v`. */ while (iterv) { UvElement *next = iterv->next; - luv = BM_ELEM_CD_GET_VOID_P(iterv->l, cd_loop_uv_offset); - const float *uv2 = luv->uv; - const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, cd_loop_uv_offset); - /* Check if the uv loops share the same selection state (if not, they are not connected as - * they have been ripped or other edit commands have separated them). */ - const bool connected = (uv_vert_sel == uv2_vert_sel) && - compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT); + bool connected = true; /* Assume connected unless we can prove otherwise. */ + + if (connected) { + /* Are the two UVs close together? */ + const float *uv2 = luv->uv; + connected = compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT); + } + + if (connected) { + /* Check if the uv loops share the same selection state (if not, they are not connected + * as they have been ripped or other edit commands have separated them). */ + const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, cd_loop_uv_offset); + connected = (uv_vert_sel == uv2_vert_sel); + } + + if (connected && use_winding) { + connected = winding[BM_elem_index_get(iterv->l->f)] == + winding[BM_elem_index_get(v->l->f)]; + } - if (connected && (!use_winding || winding[BM_elem_index_get(iterv->l->f)] == - winding[BM_elem_index_get(v->l->f)])) { + if (connected) { if (lastv) { lastv->next = next; } @@ -881,125 +1022,18 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, } /* Write back sorted list. */ - element_map->vertex[i] = newvlist; + element_map->vertex[ev_index] = newvlist; } MEM_SAFE_FREE(winding); + /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. + * Now we should sort uv's in islands. */ if (do_islands) { - uint *map; - UvElement *islandbuf; - - int nislands = 0, islandbufsize = 0; - - /* map holds the map from current vmap->buf to the new, sorted map */ - map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap"); - BMFace **stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack"); - islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer"); - /* Island number for BMFaces. */ - int *island_number = MEM_callocN(sizeof(*island_number) * bm->totface, - "uv_island_number_face"); - copy_vn_i(island_number, bm->totface, INVALID_ISLAND); - - const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ? - scene->toolsettings->selectmode & SCE_SELECT_EDGE : - scene->toolsettings->uv_selectmode & UV_SELECT_EDGE; - if (use_uv_edge_connectivity) { - nislands = bm_uv_edge_select_build_islands( - element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset); - islandbufsize = totuv; - } - - /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. - * Now we should sort uv's in islands. */ - for (i = 0; i < totuv; i++) { - if (element_map->storage[i].island == INVALID_ISLAND) { - int stacksize = 0; - element_map->storage[i].island = nislands; - stack[0] = element_map->storage[i].l->f; - island_number[BM_elem_index_get(stack[0])] = nislands; - stacksize = 1; - - while (stacksize > 0) { - efa = stack[--stacksize]; - - BMLoop *l; - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - continue; - } - - UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)]; - - for (UvElement *element = initelement; element; element = element->next) { - if (element->separate) { - initelement = element; - } - - if (element->l->f == efa) { - /* found the uv corresponding to our face and vertex. - * Now fill it to the buffer */ - bm_uv_assign_island( - element_map, element, nislands, map, islandbuf, islandbufsize++); - - for (element = initelement; element; element = element->next) { - if (element->separate && element != initelement) { - break; - } - - if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) { - stack[stacksize++] = element->l->f; - island_number[BM_elem_index_get(element->l->f)] = nislands; - } - } - break; - } - } - } - } - - nislands++; - } - } - - MEM_SAFE_FREE(island_number); - - /* remap */ - for (i = 0; i < bm->totvert; i++) { - /* important since we may do selection only. Some of these may be NULL */ - if (element_map->vertex[i]) { - element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]]; - } - } - - element_map->islandIndices = MEM_callocN(sizeof(*element_map->islandIndices) * nislands, - "UvElementMap_island_indices"); - j = 0; - for (i = 0; i < totuv; i++) { - UvElement *element = element_map->storage[i].next; - if (element == NULL) { - islandbuf[map[i]].next = NULL; - } - else { - islandbuf[map[i]].next = &islandbuf[map[element - element_map->storage]]; - } - - if (islandbuf[i].island != j) { - j++; - element_map->islandIndices[j] = i; - } - } - - MEM_SAFE_FREE(element_map->storage); - element_map->storage = islandbuf; - islandbuf = NULL; - element_map->totalIslands = nislands; - MEM_SAFE_FREE(stack); - MEM_SAFE_FREE(map); + bm_uv_build_islands(element_map, bm, scene, uv_selected); } - BLI_buffer_free(&tf_uv_buf); - + /* TODO: Confirm element_map->total_unique_uvs doesn't require recalculating. */ element_map->total_unique_uvs = 0; for (int i = 0; i < element_map->total_uvs; i++) { if (element_map->storage[i].separate) { @@ -1028,7 +1062,10 @@ void BM_uv_element_map_free(UvElementMap *element_map) if (element_map) { MEM_SAFE_FREE(element_map->storage); MEM_SAFE_FREE(element_map->vertex); - MEM_SAFE_FREE(element_map->islandIndices); + MEM_SAFE_FREE(element_map->head_table); + MEM_SAFE_FREE(element_map->island_indices); + MEM_SAFE_FREE(element_map->island_total_uvs); + MEM_SAFE_FREE(element_map->island_total_unique_uvs); MEM_SAFE_FREE(element_map); } } @@ -1524,7 +1561,7 @@ void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params) } if (params->is_destructive) { - /* TODO(campbell): we may be able to remove this now! */ + /* TODO(@campbellbarton): we may be able to remove this now! */ // BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP); } else { diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc index 9e28e1bafdd..b1004b23a21 100644 --- a/source/blender/editors/mesh/meshtools.cc +++ b/source/blender/editors/mesh/meshtools.cc @@ -1329,6 +1329,7 @@ bool ED_mesh_pick_face_vert( */ struct VertPickData { const MVert *mvert; + const bool *hide_vert; const float *mval_f; /* [2] */ ARegion *region; @@ -1343,16 +1344,16 @@ static void ed_mesh_pick_vert__mapFunc(void *userData, const float UNUSED(no[3])) { VertPickData *data = static_cast<VertPickData *>(userData); - if ((data->mvert[index].flag & ME_HIDE) == 0) { - float sco[2]; - - if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) == - V3D_PROJ_RET_OK) { - const float len = len_manhattan_v2v2(data->mval_f, sco); - if (len < data->len_best) { - data->len_best = len; - data->v_idx_best = index; - } + if (data->hide_vert && data->hide_vert[index]) { + return; + } + float sco[2]; + if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) == + V3D_PROJ_RET_OK) { + const float len = len_manhattan_v2v2(data->mval_f, sco); + if (len < data->len_best) { + data->len_best = len; + data->v_idx_best = index; } } } @@ -1416,6 +1417,8 @@ bool ED_mesh_pick_vert( data.mval_f = mval_f; data.len_best = FLT_MAX; data.v_idx_best = -1; + data.hide_vert = (const bool *)CustomData_get_layer_named( + &me_eval->vdata, CD_PROP_BOOL, ".hide_vert"); BKE_mesh_foreach_mapped_vert(me_eval, ed_mesh_pick_vert__mapFunc, &data, MESH_FOREACH_NOP); diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 06a649e5b6c..6a5d620b546 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -744,7 +744,7 @@ Base *ED_mball_base_and_elem_from_select_buffer(Base **bases, const uint hit_object = select_id & 0xFFFF; Base *base = NULL; MetaElem *ml = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ + /* TODO(@campbellbarton): optimize, eg: sort & binary search. */ for (uint base_index = 0; base_index < bases_len; base_index++) { if (bases[base_index]->object->runtime.select_id == hit_object) { base = bases[base_index]; diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index c3d8fb9cfe5..6f7fc2efa61 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -1128,7 +1128,7 @@ static int object_select_all_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } if (any_visible == false) { - /* TODO(campbell): Looks like we could remove this, + /* TODO(@campbellbarton): Looks like we could remove this, * if not comment should say why its needed. */ return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 17b7fe7fe5e..eb5aaf7ef61 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -891,7 +891,7 @@ void ED_vgroup_vert_remove(Object *ob, bDeformGroup *dg, int vertnum) * deform group. */ - /* TODO(campbell): This is slow in a loop, better pass def_nr directly, + /* TODO(@campbellbarton): This is slow in a loop, better pass def_nr directly, * but leave for later. */ const ListBase *defbase = BKE_object_defgroup_list(ob); const int def_nr = BLI_findindex(defbase, dg); @@ -1034,6 +1034,7 @@ static void vgroup_select_verts(Object *ob, int select) } else { if (me->dvert) { + const bool *hide_vert = CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"); MVert *mv; MDeformVert *dv; int i; @@ -1042,7 +1043,7 @@ static void vgroup_select_verts(Object *ob, int select) dv = me->dvert; for (i = 0; i < me->totvert; i++, mv++, dv++) { - if (!(mv->flag & ME_HIDE)) { + if (hide_vert != NULL && !hide_vert[i]) { if (BKE_defvert_find_index(dv, def_nr)) { if (select) { mv->flag |= SELECT; @@ -1118,7 +1119,7 @@ static void vgroup_duplicate(Object *ob) BKE_object_defgroup_active_index_set(ob, BLI_listbase_count(defbase)); icdg = BKE_object_defgroup_active_index_get(ob) - 1; - /* TODO(campbell): we might want to allow only copy selected verts here? */ + /* TODO(@campbellbarton): we might want to allow only copy selected verts here? */ ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false); if (dvert_array) { @@ -1930,7 +1931,11 @@ static void vgroup_smooth_subset(Object *ob, #define IS_BM_VERT_READ(v) (use_hide ? (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == 0) : true) #define IS_BM_VERT_WRITE(v) (use_select ? (BM_elem_flag_test(v, BM_ELEM_SELECT) != 0) : true) -#define IS_ME_VERT_READ(v) (use_hide ? (((v)->flag & ME_HIDE) == 0) : true) + const bool *hide_vert = me ? (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert") : + NULL; + +#define IS_ME_VERT_READ(v) (use_hide ? (hide_vert && hide_vert[v]) : true) #define IS_ME_VERT_WRITE(v) (use_select ? (((v)->flag & SELECT) != 0) : true) /* initialize used verts */ @@ -1956,8 +1961,8 @@ static void vgroup_smooth_subset(Object *ob, if (IS_ME_VERT_WRITE(v)) { for (int j = 0; j < emap[i].count; j++) { const MEdge *e = &me->medge[emap[i].indices[j]]; - const MVert *v_other = &me->mvert[(e->v1 == i) ? e->v2 : e->v1]; - if (IS_ME_VERT_READ(v_other)) { + const int i_other = (e->v1 == i) ? e->v2 : e->v1; + if (IS_ME_VERT_READ(i_other)) { STACK_PUSH(verts_used, i); break; } @@ -2031,9 +2036,7 @@ static void vgroup_smooth_subset(Object *ob, for (j = 0; j < emap[i].count; j++) { MEdge *e = &me->medge[emap[i].indices[j]]; const int i_other = (e->v1 == i ? e->v2 : e->v1); - MVert *v_other = &me->mvert[i_other]; - - if (IS_ME_VERT_READ(v_other)) { + if (IS_ME_VERT_READ(i_other)) { WEIGHT_ACCUMULATE; } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 08c8c863729..73195168d38 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -110,7 +110,7 @@ ScrArea *area_split(const wmWindow *win, return NULL; } - /* NOTE(campbell): regarding (fac > 0.5f) checks below. + /* NOTE(@campbellbarton): regarding (fac > 0.5f) checks below. * normally it shouldn't matter which is used since the copy should match the original * however with viewport rendering and python console this isn't the case. */ diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index cb29f15420c..fc3ac53ef0b 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -220,7 +220,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo workspace_new->order = workspace_old->order; BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids); - /* TODO(campbell): tools */ + /* TODO(@campbellbarton): tools */ LISTBASE_FOREACH (WorkSpaceLayout *, layout_old, &workspace_old->layouts) { WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate( diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index edb0f1cda4d..b170280ccf3 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -37,8 +37,8 @@ set(SRC curves_sculpt_ops.cc curves_sculpt_pinch.cc curves_sculpt_puff.cc - curves_sculpt_selection_paint.cc curves_sculpt_selection.cc + curves_sculpt_selection_paint.cc curves_sculpt_slide.cc curves_sculpt_smooth.cc curves_sculpt_snake_hook.cc diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index 944b3f953a0..c904d533db8 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -78,6 +78,12 @@ static void partialvis_update_mesh(Object *ob, BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK); + bool *hide_vert = CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"); + if (hide_vert == NULL) { + hide_vert = CustomData_add_layer_named( + &me->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, me->totvert, ".hide_vert"); + } + SCULPT_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); for (i = 0; i < totvert; i++) { @@ -86,16 +92,11 @@ static void partialvis_update_mesh(Object *ob, /* Hide vertex if in the hide volume. */ if (is_effected(area, planes, v->co, vmask)) { - if (action == PARTIALVIS_HIDE) { - v->flag |= ME_HIDE; - } - else { - v->flag &= ~ME_HIDE; - } + hide_vert[vert_indices[i]] = (action == PARTIALVIS_HIDE); any_changed = true; } - if (!(v->flag & ME_HIDE)) { + if (!hide_vert[vert_indices[i]]) { any_visible = true; } } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 8df5b093560..ffa931268fd 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -344,9 +344,11 @@ int SCULPT_active_face_set_get(SculptSession *ss) void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible) { switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - SET_FLAG_FROM_TEST(ss->mvert[vertex.i].flag, !visible, ME_HIDE); + case PBVH_FACES: { + bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh); + hide_vert[vertex.i] = visible; break; + } case PBVH_BMESH: { BMVert *v = (BMVert *)vertex.i; BM_elem_flag_set(v, BM_ELEM_HIDDEN, !visible); @@ -360,8 +362,10 @@ void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visib bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - return !(ss->mvert[vertex.i].flag & ME_HIDE); + case PBVH_FACES: { + const bool *hide_vert = BKE_pbvh_get_vert_hide(ss->pbvh); + return hide_vert == NULL || !hide_vert[vertex.i]; + } case PBVH_BMESH: return !BM_elem_flag_test((BMVert *)vertex.i, BM_ELEM_HIDDEN); case PBVH_GRIDS: { @@ -5656,7 +5660,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot) ot->cancel = sculpt_brush_stroke_cancel; /* Flags (sculpt does own undo? (ton)). */ - ot->flag = OPTYPE_BLOCKING; + ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO; /* Properties. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 4f91d2215fb..40b4b74a441 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -227,8 +227,9 @@ static void SCULPT_dynamic_topology_disable_ex( me->face_sets_color_default = 1; /* Sync the visibility to vertices manually as the pmap is still not initialized. */ - for (int i = 0; i < me->totvert; i++) { - me->mvert[i].flag &= ~ME_HIDE; + bool *hide_vert = (bool *)CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"); + if (hide_vert != NULL) { + memset(hide_vert, 0, sizeof(bool) * me->totvert); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index dc814d6d22f..04b2b2f04bf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -346,14 +346,13 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; - if (unode->maxvert) { - MVert *mvert = ss->mvert; + bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh); + if (unode->maxvert) { for (int i = 0; i < unode->totvert; i++) { - MVert *v = &mvert[unode->index[i]]; - if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) { + if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != hide_vert[i]) { BLI_BITMAP_FLIP(unode->vert_hidden, i); - v->flag ^= ME_HIDE; + hide_vert[unode->index[i]] = !hide_vert[i]; modified_vertices[unode->index[i]] = true; } } @@ -1247,6 +1246,11 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) PBVH *pbvh = ob->sculpt->pbvh; PBVHNode *node = unode->node; + const bool *hide_vert = BKE_pbvh_get_vert_hide(pbvh); + if (hide_vert == NULL) { + return; + } + if (unode->grids) { /* Already stored during allocation. */ } @@ -1258,7 +1262,7 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert); BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); for (int i = 0; i < allvert; i++) { - BLI_BITMAP_SET(unode->vert_hidden, i, mvert[vert_indices[i]].flag & ME_HIDE); + BLI_BITMAP_SET(unode->vert_hidden, i, hide_vert[vert_indices[i]]); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index f2017e68b4c..14b06f888fe 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -518,13 +518,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm /* Count 'unique' UV's */ int unique_uvs = data->elementMap->total_unique_uvs; if (do_island_optimization) { - unique_uvs = 0; - for (int i = 0; i < data->elementMap->total_uvs; i++) { - if (data->elementMap->storage[i].separate && - (data->elementMap->storage[i].island == island_index)) { - unique_uvs++; - } - } + unique_uvs = data->elementMap->island_total_unique_uvs[island_index]; } /* Allocate the unique uv buffers */ @@ -572,6 +566,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm uniqueUv[element - data->elementMap->storage] = counter; } } + BLI_assert(counter + 1 == unique_uvs); /* Now, on to generate our uv connectivity data */ counter = 0; @@ -629,11 +624,13 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm } /* fill the edges with data */ - int i = 0; - GHASH_ITER (gh_iter, edgeHash) { - data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); + { + int i = 0; + GHASH_ITER (gh_iter, edgeHash) { + data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); + } + data->totalUvEdges = BLI_ghash_len(edgeHash); } - data->totalUvEdges = BLI_ghash_len(edgeHash); /* cleanup temporary stuff */ BLI_ghash_free(edgeHash, NULL, NULL); diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index d1a8592ae9d..ed9d86e1a4e 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -919,7 +919,7 @@ static const EnumPropertyItem prop_column_select_types[] = { /* ------------------- */ /* Selects all visible keyframes between the specified markers */ -/* TODO(campbell): this is almost an _exact_ duplicate of a function of the same name in +/* TODO(@campbellbarton): this is almost an _exact_ duplicate of a function of the same name in * graph_select.c should de-duplicate. */ static void markers_selectkeys_between(bAnimContext *ac) { diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index a36bd5c1461..0ce3e1a797a 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -1128,7 +1128,7 @@ static const EnumPropertyItem prop_column_select_types[] = { /* ------------------- */ /* Selects all visible keyframes between the specified markers */ -/* TODO(campbell): this is almost an _exact_ duplicate of a function of the same name in +/* TODO(@campbellbarton): this is almost an _exact_ duplicate of a function of the same name in * action_select.c should de-duplicate. */ static void markers_selectkeys_between(bAnimContext *ac) { diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index badcccca87b..26fddda8c22 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -50,8 +50,8 @@ set(LIB bf_editor_screen ) -if(WITH_COMPOSITOR) - add_definitions(-DWITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_OPENIMAGEDENOISE) diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 78ec057f921..b9f79303a06 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -48,11 +48,11 @@ set(SRC tree/tree_element_anim_data.cc tree/tree_element_collection.cc tree/tree_element_driver.cc - tree/tree_element_label.cc tree/tree_element_gpencil_layer.cc tree/tree_element_id.cc tree/tree_element_id_library.cc tree/tree_element_id_scene.cc + tree/tree_element_label.cc tree/tree_element_nla.cc tree/tree_element_overrides.cc tree/tree_element_rna.cc @@ -68,11 +68,11 @@ set(SRC tree/tree_element_anim_data.hh tree/tree_element_collection.hh tree/tree_element_driver.hh - tree/tree_element_label.hh tree/tree_element_gpencil_layer.hh tree/tree_element_id.hh tree/tree_element_id_library.hh tree/tree_element_id_scene.hh + tree/tree_element_label.hh tree/tree_element_nla.hh tree/tree_element_overrides.hh tree/tree_element_rna.hh diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index 7d0a0a921e4..02d54e4f702 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -364,7 +364,7 @@ void outliner_collection_delete( const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id); BLI_assert(id_type->owner_get != nullptr); - ID *scene_owner = id_type->owner_get(bmain, &parent->id); + ID *scene_owner = id_type->owner_get(bmain, &parent->id, NULL); BLI_assert(GS(scene_owner->name) == ID_SCE); if (ID_IS_LINKED(scene_owner) || ID_IS_OVERRIDE_LIBRARY(scene_owner)) { skip = true; @@ -597,7 +597,7 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op) const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id); BLI_assert(id_type->owner_get != nullptr); - Scene *scene_owner = (Scene *)id_type->owner_get(bmain, &parent->id); + Scene *scene_owner = (Scene *)id_type->owner_get(bmain, &parent->id, NULL); BLI_assert(scene_owner != nullptr); BLI_assert(GS(scene_owner->id.name) == ID_SCE); diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 8bc1ed8c84e..e67eab4e432 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -2855,7 +2855,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) } /** - * \return Return true if the element has an icon that was drawn, false if it doesn't have an icon. + * \return true if the element has an icon that was drawn, false if it doesn't have an icon. */ static bool tselem_draw_icon(uiBlock *block, int xmax, diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index 18173b37123..5362782dd84 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -530,6 +530,8 @@ void OUTLINER_OT_operation(struct wmOperatorType *ot); void OUTLINER_OT_scene_operation(struct wmOperatorType *ot); void OUTLINER_OT_object_operation(struct wmOperatorType *ot); void OUTLINER_OT_lib_operation(struct wmOperatorType *ot); +void OUTLINER_OT_liboverride_operation(struct wmOperatorType *ot); +void OUTLINER_OT_liboverride_troubleshoot_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_remap(struct wmOperatorType *ot); void OUTLINER_OT_id_copy(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc index 8baac45666e..b384c41aa69 100644 --- a/source/blender/editors/space_outliner/outliner_ops.cc +++ b/source/blender/editors/space_outliner/outliner_ops.cc @@ -29,6 +29,8 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_object_operation); WM_operatortype_append(OUTLINER_OT_lib_operation); WM_operatortype_append(OUTLINER_OT_lib_relocate); + WM_operatortype_append(OUTLINER_OT_liboverride_operation); + WM_operatortype_append(OUTLINER_OT_liboverride_troubleshoot_operation); WM_operatortype_append(OUTLINER_OT_id_operation); WM_operatortype_append(OUTLINER_OT_id_delete); WM_operatortype_append(OUTLINER_OT_id_remap); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index c408eca654c..f51a70af3bc 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -453,6 +453,99 @@ static void outliner_do_libdata_operation(bContext *C, }); } +typedef enum eOutlinerLibOpSelectionSet { + /* Only selected items. */ + OUTLINER_LIB_SELECTIONSET_SELECTED, + /* Only content 'inside' selected items (their sub-tree). */ + OUTLINER_LIB_LIB_SELECTIONSET_CONTENT, + /* Combining both options above. */ + OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT, +} eOutlinerLibOpSelectionSet; + +static const EnumPropertyItem prop_lib_op_selection_set[] = { + {OUTLINER_LIB_SELECTIONSET_SELECTED, + "SELECTED", + 0, + "Selected", + "Apply the operation over selected data-blocks only"}, + {OUTLINER_LIB_LIB_SELECTIONSET_CONTENT, + "CONTENT", + 0, + "Content", + "Apply the operation over content of the selected items only (the data-blocks in their " + "sub-tree)"}, + {OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT, + "SELECTED_AND_CONTENT", + 0, + "Selected & Content", + "Apply the operation over selected data-blocks and all their dependencies"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static void outliner_do_libdata_operation_selection_set(bContext *C, + ReportList *reports, + Scene *scene, + SpaceOutliner *space_outliner, + const ListBase &subtree, + const bool has_parent_selected, + outliner_operation_fn operation_fn, + eOutlinerLibOpSelectionSet selection_set, + void *user_data) +{ + const bool do_selected = ELEM(selection_set, + OUTLINER_LIB_SELECTIONSET_SELECTED, + OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT); + const bool do_content = ELEM(selection_set, + OUTLINER_LIB_LIB_SELECTIONSET_CONTENT, + OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT); + + LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { + /* Get needed data out in case element gets freed. */ + TreeStoreElem *tselem = TREESTORE(element); + const ListBase subtree = element->subtree; + + bool is_selected = tselem->flag & TSE_SELECTED; + if ((is_selected && do_selected) || (has_parent_selected && do_content)) { + if (((tselem->type == TSE_SOME_ID) && (element->idcode != 0)) || + tselem->type == TSE_LAYER_COLLECTION) { + TreeStoreElem *tsep = element->parent ? TREESTORE(element->parent) : nullptr; + operation_fn(C, reports, scene, element, tsep, tselem, user_data); + } + } + + /* Don't access element from now on, it may be freed. Note that the open/collapsed state may + * also have been changed in the visitor callback. */ + outliner_do_libdata_operation_selection_set(C, + reports, + scene, + space_outliner, + subtree, + is_selected || has_parent_selected, + operation_fn, + selection_set, + user_data); + } +} + +static void outliner_do_libdata_operation_selection_set(bContext *C, + ReportList *reports, + Scene *scene, + SpaceOutliner *space_outliner, + outliner_operation_fn operation_fn, + eOutlinerLibOpSelectionSet selection_set, + void *user_data) +{ + outliner_do_libdata_operation_selection_set(C, + reports, + scene, + space_outliner, + space_outliner->tree, + false, + operation_fn, + selection_set, + user_data); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -842,6 +935,20 @@ struct OutlinerLibOverrideData { id_hierarchy_root_reference); value.append(id_root_data); } + void id_root_set(ID *id_hierarchy_root_reference) + { + OutlinerLiboverrideDataIDRoot id_root_data; + id_root_data.id_root_reference = nullptr; + id_root_data.id_hierarchy_root_override = nullptr; + id_root_data.id_instance_hint = nullptr; + id_root_data.is_override_instancing_object = false; + + Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default( + id_hierarchy_root_reference); + if (value.is_empty()) { + value.append(id_root_data); + } + } }; /* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override @@ -860,11 +967,21 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *C, const bool do_hierarchy = data->do_hierarchy; ID *id_root_reference = tselem->id; + if (!BKE_idtype_idcode_is_linkable(GS(id_root_reference->name)) || + (id_root_reference->flag & (LIB_EMBEDDED_DATA | LIB_EMBEDDED_DATA_LIB_OVERRIDE)) != 0) { + return; + } + BLI_assert(do_hierarchy); UNUSED_VARS_NDEBUG(do_hierarchy); data->selected_id_uid.add(id_root_reference->session_uuid); + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root_reference) && !ID_IS_LINKED(id_root_reference)) { + id_root_reference->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + return; + } + if (GS(id_root_reference->name) == ID_GR && (tselem->flag & TSE_CLOSED) != 0) { /* If selected element is a (closed) collection, check all of its objects recursively, and also * consider the armature ones as 'selected' (i.e. to not become system overrides). */ @@ -983,6 +1100,17 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *C, return; } + /* While ideally this should not be needed, in practice user almost _never_ wants to actually + * create liboverrides for all data under a selected hierarchy node, and this has currently a + * dreadful consequences over performances (since it would call + * #BKE_lib_override_library_create over _all_ items in the hierarchy). So only the clearing of + * the system override flag is supported for non-selected items for now. + */ + const bool is_selected = tselem->flag & TSE_SELECTED; + if (!is_selected && data->id_hierarchy_roots.contains(id_hierarchy_root_reference)) { + return; + } + data->id_root_add(id_hierarchy_root_reference, id_root_reference, id_instance_hint, @@ -1133,23 +1261,6 @@ static void id_override_library_create_hierarchy_process(bContext *C, FOREACH_MAIN_ID_END; } -static void id_override_library_toggle_flag_fn(bContext *UNUSED(C), - ReportList *UNUSED(reports), - Scene *UNUSED(scene), - TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *user_data) -{ - BLI_assert(TSE_IS_REAL_ID(tselem)); - ID *id = tselem->id; - - if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - const uint flag = POINTER_AS_UINT(user_data); - id->override_library->flag ^= flag; - } -} - static void id_override_library_reset_fn(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -1181,10 +1292,10 @@ static void id_override_library_reset_fn(bContext *C, } } -static void id_override_library_resync_fn(bContext *C, - ReportList *reports, - Scene *scene, - TreeElement *te, +static void id_override_library_resync_fn(bContext *UNUSED(C), + ReportList *UNUSED(reports), + Scene *UNUSED(scene), + TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *user_data) @@ -1192,44 +1303,53 @@ static void id_override_library_resync_fn(bContext *C, BLI_assert(TSE_IS_REAL_ID(tselem)); ID *id_root = tselem->id; OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data); - const bool do_hierarchy_enforce = data->do_resync_hierarchy_enforce; - if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { - Main *bmain = CTX_data_main(C); + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { + CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name); + } - id_root->tag |= LIB_TAG_DOIT; + if (id_root->override_library->hierarchy_root != nullptr) { + id_root = id_root->override_library->hierarchy_root; + } - /* Tag all linked parents in tree hierarchy to be also overridden. */ - while ((te = te->parent) != nullptr) { - if (!TSE_IS_REAL_ID(te->store_elem)) { - continue; - } - if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) { - break; - } - te->store_elem->id->tag |= LIB_TAG_DOIT; - } + data->id_root_set(id_root); +} - BlendFileReadReport report{}; - report.reports = reports; - BKE_lib_override_library_resync( - bmain, scene, CTX_data_view_layer(C), id_root, nullptr, do_hierarchy_enforce, &report); +/* Resync a hierarchy of library overrides. */ +static void id_override_library_resync_hierarchy_process(bContext *C, + ReportList *reports, + OutlinerLibOverrideData &data) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + const bool do_hierarchy_enforce = data.do_resync_hierarchy_enforce; - WM_event_add_notifier(C, NC_WINDOW, nullptr); - } - else { - CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name); + BlendFileReadReport report{}; + report.reports = reports; + + for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) { + BKE_lib_override_library_resync(bmain, + scene, + CTX_data_view_layer(C), + id_hierarchy_root, + nullptr, + do_hierarchy_enforce, + &report); } + + WM_event_add_notifier(C, NC_WINDOW, nullptr); } -static void id_override_library_clear_hierarchy_fn(bContext *C, +static void id_override_library_clear_hierarchy_fn(bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), - TreeElement *te, + TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, - void *UNUSED(user_data)) + void *user_data) { + OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); + BLI_assert(TSE_IS_REAL_ID(tselem)); ID *id_root = tselem->id; @@ -1238,22 +1358,23 @@ static void id_override_library_clear_hierarchy_fn(bContext *C, return; } - Main *bmain = CTX_data_main(C); + if (id_root->override_library->hierarchy_root != nullptr) { + id_root = id_root->override_library->hierarchy_root; + } - id_root->tag |= LIB_TAG_DOIT; + data->id_root_set(id_root); +} - /* Tag all override parents in tree hierarchy to be also processed. */ - while ((te = te->parent) != nullptr) { - if (!TSE_IS_REAL_ID(te->store_elem)) { - continue; - } - if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) { - break; - } - te->store_elem->id->tag |= LIB_TAG_DOIT; - } +/* Clear (delete) a hierarchy of library overrides. */ +static void id_override_library_clear_hierarchy_process(bContext *C, + ReportList *UNUSED(reports), + OutlinerLibOverrideData &data) +{ + Main *bmain = CTX_data_main(C); - BKE_lib_override_library_delete(bmain, id_root); + for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) { + BKE_lib_override_library_delete(bmain, id_hierarchy_root); + } WM_event_add_notifier(C, NC_WINDOW, nullptr); } @@ -1494,6 +1615,250 @@ static void refreshdrivers_animdata_fn(int UNUSED(event), /** \} */ /* -------------------------------------------------------------------- */ +/** \name Library Overrides Operation Menu. + * \{ */ + +enum eOutlinerLibOverrideOpTypes { + OUTLINER_LIBOVERRIDE_OP_INVALID = 0, + + OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY, + OUTLINER_LIBOVERRIDE_OP_RESET, + OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE, + + OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY, + OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE, + OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY, +}; + +static const EnumPropertyItem prop_liboverride_op_types[] = { + {OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY, + "OVERRIDE_LIBRARY_CREATE_HIERARCHY", + 0, + "Create", + "Make a local override of the selected linked data-blocks, and their hierarchy of " + "dependencies"}, + {OUTLINER_LIBOVERRIDE_OP_RESET, + "OVERRIDE_LIBRARY_RESET", + 0, + "Reset", + "Reset the selected local override to their linked references values"}, + {OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE, + "OVERRIDE_LIBRARY_CLEAR_SINGLE", + 0, + "Clear", + "Delete the selected local overrides and relink their usages to the linked data-blocks if " + "possible, else reset them and mark them as non editable"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static const EnumPropertyItem prop_liboverride_troubleshoot_op_types[] = { + {OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY, + "OVERRIDE_LIBRARY_RESYNC_HIERARCHY", + 0, + "Resync", + "Rebuild the selected local overrides from their linked references, as well as their " + "hierarchies of dependencies"}, + {OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE, + "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE", + 0, + "Resync Enforce", + "Rebuild the selected local overrides from their linked references, as well as their " + "hierarchies of dependencies, enforcing these hierarchies to match the linked data (i.e. " + "ignoring existing overrides on data-blocks pointer properties)"}, + {OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY, + "OVERRIDE_LIBRARY_DELETE_HIERARCHY", + 0, + "Delete", + "Delete the selected local overrides (including their hierarchies of override dependencies) " + "and relink their usages to the linked data-blocks"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static bool outliner_liboverride_operation_poll(bContext *C) +{ + if (!outliner_operation_tree_element_poll(C)) { + return false; + } + return true; +} + +static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; + + /* check for invalid states */ + if (space_outliner == nullptr) { + return OPERATOR_CANCELLED; + } + + TreeElement *te = get_target_element(space_outliner); + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); + + const eOutlinerLibOpSelectionSet selection_set = static_cast<eOutlinerLibOpSelectionSet>( + RNA_enum_get(op->ptr, "selection_set")); + const eOutlinerLibOverrideOpTypes event = static_cast<eOutlinerLibOverrideOpTypes>( + RNA_enum_get(op->ptr, "type")); + switch (event) { + case OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + override_data.do_fully_editable = false; + + outliner_do_libdata_operation_selection_set( + C, + op->reports, + scene, + space_outliner, + id_override_library_create_hierarchy_pre_process_fn, + selection_set, + &override_data); + + id_override_library_create_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Overridden Data Hierarchy"); + break; + } + case OUTLINER_LIBOVERRIDE_OP_RESET: { + OutlinerLibOverrideData override_data{}; + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_reset_fn, + selection_set, + &override_data); + ED_undo_push(C, "Reset Overridden Data"); + break; + } + case OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE: { + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_clear_single_fn, + selection_set, + nullptr); + ED_undo_push(C, "Clear Overridden Data"); + break; + } + + case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_resync_fn, + OUTLINER_LIB_SELECTIONSET_SELECTED, + &override_data); + + id_override_library_resync_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Resync Overridden Data Hierarchy"); + break; + } + case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + override_data.do_resync_hierarchy_enforce = true; + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_resync_fn, + OUTLINER_LIB_SELECTIONSET_SELECTED, + &override_data); + + id_override_library_resync_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Resync Overridden Data Hierarchy Enforce"); + break; + } + case OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + outliner_do_libdata_operation_selection_set(C, + op->reports, + scene, + space_outliner, + id_override_library_clear_hierarchy_fn, + OUTLINER_LIB_SELECTIONSET_SELECTED, + nullptr); + + id_override_library_clear_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Delete Overridden Data Hierarchy"); + break; + } + default: + /* Invalid - unhandled. */ + break; + } + + /* wrong notifier still... */ + WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); + + /* XXX: this is just so that outliner is always up to date. */ + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, nullptr); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_liboverride_operation(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Outliner Library Override Operation"; + ot->idname = "OUTLINER_OT_liboverride_operation"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = outliner_liboverride_operation_exec; + ot->poll = outliner_liboverride_operation_poll; + + ot->flag = 0; + + RNA_def_enum(ot->srna, "type", prop_liboverride_op_types, 0, "Library Override Operation", ""); + ot->prop = RNA_def_enum(ot->srna, + "selection_set", + prop_lib_op_selection_set, + 0, + "Selection Set", + "Over which part of the tree items to apply the operation"); +} + +void OUTLINER_OT_liboverride_troubleshoot_operation(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Outliner Library Override Troubleshoot Operation"; + ot->idname = "OUTLINER_OT_liboverride_troubleshoot_operation"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = outliner_liboverride_operation_exec; + ot->poll = outliner_liboverride_operation_poll; + + ot->flag = 0; + + RNA_def_enum(ot->srna, + "type", + prop_liboverride_troubleshoot_op_types, + 0, + "Library Override Troubleshoot Operation", + ""); + ot->prop = RNA_def_enum(ot->srna, + "selection_set", + prop_lib_op_selection_set, + 0, + "Selection Set", + "Over which part of the tree items to apply the operation"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Object Operation Utilities * \{ */ @@ -2088,15 +2453,6 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_UNLINK, OUTLINER_IDOP_LOCAL, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, - OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, - OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, OUTLINER_IDOP_REMAP, @@ -2123,59 +2479,6 @@ static const EnumPropertyItem prop_id_op_types[] = { "Remap Users", "Make all users of selected data-blocks to use instead current (clicked) one"}, RNA_ENUM_ITEM_SEPR, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, - "OVERRIDE_LIBRARY_CREATE", - 0, - "Make Library Override Single", - "Make a single, out-of-hierarchy local override of this linked data-block - only applies to " - "active Outliner item"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, - "OVERRIDE_LIBRARY_CREATE_HIERARCHY", - 0, - "Make Library Override Hierarchy", - "Make a local override of this linked data-block, and its hierarchy of dependencies - only " - "applies to active Outliner item"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, - "OVERRIDE_LIBRARY_MAKE_EDITABLE", - 0, - "Make Library Override Editable", - "Make the library override data-block editable"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, - "OVERRIDE_LIBRARY_RESET", - 0, - "Reset Library Override Single", - "Reset this local override to its linked values"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, - "OVERRIDE_LIBRARY_RESET_HIERARCHY", - 0, - "Reset Library Override Hierarchy", - "Reset this local override to its linked values, as well as its hierarchy of dependencies"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, - "OVERRIDE_LIBRARY_RESYNC_HIERARCHY", - 0, - "Resync Library Override Hierarchy", - "Rebuild this local override from its linked reference, as well as its hierarchy of " - "dependencies"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE, - "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE", - 0, - "Resync Library Override Hierarchy Enforce", - "Rebuild this local override from its linked reference, as well as its hierarchy of " - "dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting " - "overrides on data-blocks pointer properties)"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE, - "OVERRIDE_LIBRARY_CLEAR_SINGLE", - 0, - "Clear Library Override Single", - "Delete this local override and relink its usages to the linked data-blocks if possible, " - "else reset it and mark it as non editable"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY, - "OVERRIDE_LIBRARY_CLEAR_HIERARCHY", - 0, - "Clear Library Override Hierarchy", - "Delete this local override (including its hierarchy of override dependencies) and relink " - "its usages to the linked data-blocks"}, - RNA_ENUM_ITEM_SEPR, {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""}, {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""}, RNA_ENUM_ITEM_SEPR, @@ -2208,33 +2511,6 @@ static bool outliner_id_operation_item_poll(bContext *C, } switch (enum_value) { - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: - if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) { - return true; - } - return false; - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: - if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) { - return true; - } - return false; - case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: - if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) { - if (tselem->id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) { - return true; - } - } - return false; - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: - if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) { - return true; - } - return false; case OUTLINER_IDOP_SINGLE: if (ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) { return true; @@ -2347,95 +2623,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Localized Data"); break; } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = false; - override_data.do_fully_editable = true; - - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - id_override_library_create_hierarchy_pre_process_fn, - &override_data); - - id_override_library_create_hierarchy_process(C, op->reports, override_data); - - ED_undo_push(C, "Overridden Data"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - override_data.do_fully_editable = U.experimental.use_override_new_fully_editable; - - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - id_override_library_create_hierarchy_pre_process_fn, - &override_data); - - id_override_library_create_hierarchy_process(C, op->reports, override_data); - - ED_undo_push(C, "Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - id_override_library_toggle_flag_fn, - POINTER_FROM_UINT(IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED)); - - ED_undo_push(C, "Make Overridden Data Editable"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: { - OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); - ED_undo_push(C, "Reset Overridden Data"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); - ED_undo_push(C, "Reset Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); - ED_undo_push(C, "Resync Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - override_data.do_resync_hierarchy_enforce = true; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); - ED_undo_push(C, "Resync Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_clear_hierarchy_fn, nullptr); - ED_undo_push(C, "Clear Overridden Data Hierarchy"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_clear_single_fn, nullptr); - ED_undo_push(C, "Clear Overridden Data Hierarchy"); - break; - } case OUTLINER_IDOP_SINGLE: { /* make single user */ switch (idlevel) { diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc index f8705c3f0ad..e0a1958795a 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc @@ -15,6 +15,7 @@ #include "BLT_translation.h" +#include "BKE_lib_override.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -80,6 +81,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData & class OverrideIDHierarchyBuilder { SpaceOutliner &space_outliner_; + Main &bmain_; MainIDRelations &id_relations_; struct HierarchyBuildData { @@ -93,8 +95,10 @@ class OverrideIDHierarchyBuilder { }; public: - OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, MainIDRelations &id_relations) - : space_outliner_(space_outliner), id_relations_(id_relations) + OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, + Main &bmain, + MainIDRelations &id_relations) + : space_outliner_(space_outliner), bmain_(bmain), id_relations_(id_relations) { } @@ -115,7 +119,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( * returning. */ BKE_main_relations_create(bmain, 0); - OverrideIDHierarchyBuilder builder(space_outliner_, *bmain->relations); + OverrideIDHierarchyBuilder builder(space_outliner_, *bmain, *bmain->relations); /* Keep track over which ID base elements were already added, and expand them once added. */ Map<ID_Type, TreeElement *> id_base_te_map; @@ -165,7 +169,8 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id, static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, const ID &parent_id, FunctionRef<void(ID &)> fn); -static bool id_is_in_override_hierarchy(const ID &id, +static bool id_is_in_override_hierarchy(const Main &bmain, + const ID &id, const ID &relationship_parent_id, const ID &override_root_id); @@ -177,7 +182,11 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &pare build_data.parent_ids.add(&parent_id); foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) { - if (!id_is_in_override_hierarchy(id, parent_id, build_data.override_root_id_)) { + /* Some IDs can use themselves, early abort. */ + if (&id == &parent_id) { + return; + } + if (!id_is_in_override_hierarchy(bmain_, id, parent_id, build_data.override_root_id_)) { return; } @@ -276,7 +285,8 @@ static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, } } -static bool id_is_in_override_hierarchy(const ID &id, +static bool id_is_in_override_hierarchy(const Main &bmain, + const ID &id, const ID &relationship_parent_id, const ID &override_root_id) { @@ -286,20 +296,12 @@ static bool id_is_in_override_hierarchy(const ID &id, const ID *real_override_id = &id; if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) { - /* This assumes that the parent ID is always the owner of the 'embedded' one, I.e. that no - * other ID directly uses the embedded one. Should be true, but the debug code adds some checks - * to validate this assumption. */ - real_override_id = &relationship_parent_id; - -#ifndef NDEBUG - if (GS(id.name) == ID_KE) { - const Key *key = (Key *)&id; - BLI_assert(real_override_id == key->from); - } - else { - BLI_assert((id.flag & LIB_EMBEDDED_DATA) != 0); - } -#endif + /* In many cases, `relationship_parent_id` is the owner, but not always (e.g. there can be + * drivers directly between an object and a shapekey). */ + BKE_lib_override_library_get(const_cast<Main *>(&bmain), + const_cast<ID *>(&id), + const_cast<ID *>(&relationship_parent_id), + const_cast<ID **>(&real_override_id)); } if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) { diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index e19459ced61..49cabd5117f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -371,8 +371,7 @@ void OverrideRNAPathTreeBuilder::ensure_entire_collection( const char *coll_prop_path, short &index) { - AbstractTreeElement *abstract_parent = tree_element_cast<AbstractTreeElement>(&te_to_expand); - BLI_assert(abstract_parent != nullptr); + BLI_assert(tree_element_cast<AbstractTreeElement>(&te_to_expand) != nullptr); TreeElement *previous_te = nullptr; int item_idx = 0; diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c index e8c7590c1fe..a32c8a3f85a 100644 --- a/source/blender/editors/space_script/script_edit.c +++ b/source/blender/editors/space_script/script_edit.c @@ -100,7 +100,7 @@ static int script_reload_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* TODO(campbell): this crashes on netrender and keying sets, need to look into why + /* TODO(@campbellbarton): this crashes on netrender and keying sets, need to look into why * disable for now unless running in debug mode. */ /* It would be nice if we could detect when this is called from the Python diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index cb95e9a75de..9313e45a1d4 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2869,7 +2869,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "directory", directory); if (is_relative_path) { - /* TODO(campbell): shouldn't this already be relative from the filesel? + /* TODO(@campbellbarton): shouldn't this already be relative from the filesel? * (as the 'filepath' is) for now just make relative here, * but look into changing after 2.60. */ BLI_path_rel(directory, BKE_main_blendfile_path(bmain)); diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index 6ba1dcc5eb8..af0aa093e40 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -17,7 +17,7 @@ #include "sequencer_intern.h" -/* XXX(campbell): why is this function better than BLI_math version? +/* XXX(@campbellbarton): why is this function better than BLI_math version? * only difference is it does some normalize after, need to double check on this. */ static void rgb_to_yuv_normalized(const float rgb[3], float yuv[3]) { diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c index 54735a4d481..461606f63aa 100644 --- a/source/blender/editors/space_text/text_autocomplete.c +++ b/source/blender/editors/space_text/text_autocomplete.c @@ -314,7 +314,7 @@ static int doc_scroll = 0; static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *event) { - /* NOTE(campbell): this code could be refactored or rewritten. */ + /* NOTE(@campbellbarton): this code could be refactored or rewritten. */ SpaceText *st = CTX_wm_space_text(C); ScrArea *area = CTX_wm_area(C); ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a423a842019..1a2eb20d1a9 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -960,7 +960,7 @@ static void view3d_widgets(void) WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera); WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera_view); WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_empty_image); - /* TODO(campbell): Not working well enough, disable for now. */ + /* TODO(@campbellbarton): Not working well enough, disable for now. */ #if 0 WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_armature_spline); #endif @@ -1210,6 +1210,9 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) break; } break; + case NC_NODE: + ED_region_tag_redraw(region); + break; case NC_WORLD: switch (wmn->data) { case ND_WORLD_DRAW: diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 4a1bd6ba945..fb44797eded 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -495,6 +495,16 @@ static void v3d_cursor_eventstate_save_xy(SnapCursorDataIntern *cursor_snap, } #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK +static void v3d_cursor_eventstate_save_modifier(SnapCursorDataIntern *data_intern, + const wmWindowManager *wm) +{ + if (!wm || !wm->winactive) { + return; + } + const wmEvent *event = wm->winactive->eventstate; + data_intern->last_eventstate.modifier = event->modifier; +} + static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const wmWindowManager *wm) { if (!wm || !wm->winactive) { @@ -582,10 +592,14 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, { SnapCursorDataIntern *data_intern = &g_data_intern; V3DSnapCursorData *snap_data = &data_intern->snap_data; - v3d_cursor_snap_context_ensure(scene); + + const bool use_surface_nor = state->plane_orient == V3D_PLACE_ORIENT_SURFACE; + const bool use_surface_co = state->plane_depth == V3D_PLACE_DEPTH_SURFACE; + const bool calc_plane_omat = v3d_cursor_snap_calc_plane(); float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3]; eSnapMode snap_elem = SCE_SNAP_MODE_NONE; + eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene); int snap_elem_index[3] = {-1, -1, -1}; int index = -1; @@ -594,77 +608,84 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, zero_v3(face_nor); unit_m3(omat); - eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene); - data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE; - const bool calc_plane_omat = v3d_cursor_snap_calc_plane(); - if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) { - data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST; - snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST; - } + if (use_surface_nor || use_surface_co) { + v3d_cursor_snap_context_ensure(scene); + + data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE; + if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) { + data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST; + snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST; + } - snap_data->is_enabled = true; + snap_data->is_enabled = true; #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if (!(state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) { - snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, wm); - - const ToolSettings *ts = scene->toolsettings; - if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) { - snap_data->is_enabled = false; - if (!calc_plane_omat) { - snap_data->snap_elem = SCE_SNAP_MODE_NONE; - return; + if (!(state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) { + snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, wm); + + const ToolSettings *ts = scene->toolsettings; + if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) { + snap_data->is_enabled = false; + if (!calc_plane_omat) { + snap_data->snap_elem = SCE_SNAP_MODE_NONE; + return; + } + snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST; } - snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST; } - } #endif - if (snap_elements & SCE_SNAP_MODE_GEOM) { - float prev_co[3] = {0.0f}; - if (state->prevpoint) { - copy_v3_v3(prev_co, state->prevpoint); - } - else { - snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; - } + if (snap_elements & SCE_SNAP_MODE_GEOM) { + float prev_co[3] = {0.0f}; + if (state->prevpoint) { + copy_v3_v3(prev_co, state->prevpoint); + } + else { + snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; + } - eSnapEditType edit_mode_type = (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ? - SNAP_GEOM_FINAL : - (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ? - SNAP_GEOM_CAGE : - SNAP_GEOM_EDIT; - - bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : true; - - float dist_px = 12.0f * U.pixelsize; - - snap_elem = ED_transform_snap_object_project_view3d_ex( - data_intern->snap_context_v3d, - depsgraph, - region, - v3d, - snap_elements, - &(const struct SnapObjectParams){ - .snap_target_select = SCE_SNAP_TARGET_ALL, - .edit_mode_type = edit_mode_type, - .use_occlusion_test = use_occlusion_test, - }, - NULL, - mval_fl, - prev_co, - &dist_px, - co, - no, - &index, - NULL, - obmat, - face_nor); + eSnapEditType edit_mode_type = (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ? + SNAP_GEOM_FINAL : + (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ? + SNAP_GEOM_CAGE : + SNAP_GEOM_EDIT; + + bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : + true; + + float dist_px = 12.0f * U.pixelsize; + + snap_elem = ED_transform_snap_object_project_view3d_ex( + data_intern->snap_context_v3d, + depsgraph, + region, + v3d, + snap_elements, + &(const struct SnapObjectParams){ + .snap_target_select = SCE_SNAP_TARGET_ALL, + .edit_mode_type = edit_mode_type, + .use_occlusion_test = use_occlusion_test, + }, + NULL, + mval_fl, + prev_co, + &dist_px, + co, + no, + &index, + NULL, + obmat, + face_nor); + } + } +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + else { + v3d_cursor_eventstate_save_modifier(data_intern, wm); } +#endif if (calc_plane_omat) { RegionView3D *rv3d = region->regiondata; - bool orient_surface = (snap_elem != SCE_SNAP_MODE_NONE) && - (state->plane_orient == V3D_PLACE_ORIENT_SURFACE); + bool orient_surface = use_surface_nor && (snap_elem != SCE_SNAP_MODE_NONE); if (orient_surface) { copy_m3_m4(omat, obmat); } @@ -715,6 +736,10 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, } } + if (!use_surface_co) { + snap_elem = SCE_SNAP_MODE_NONE; + } + float *co_depth = (snap_elem != SCE_SNAP_MODE_NONE) ? co : scene->cursor.location; snap_elem &= ~data_intern->snap_elem_hidden; if (snap_elem == SCE_SNAP_MODE_NONE) { diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c index 3f6167d92ca..62799dd7a5c 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c @@ -37,7 +37,7 @@ * \{ */ /* - * TODO(campbell): Current conversion is a approximation (usable not correct), + * TODO(@campbellbarton): Current conversion is a approximation (usable not correct), * we'll need to take the next/previous bones into account to get the tangent directions. * First last matrices from 'BKE_pchan_bbone_spline_setup' are close but also not quite accurate * since they're not at either end-points on the curve. diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c index 35d4746608b..6256eeb9621 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.c +++ b/source/blender/editors/space_view3d/view3d_iterators.c @@ -205,6 +205,7 @@ typedef struct foreachScreenObjectVert_userData { void (*func)(void *userData, MVert *mv, const float screen_co[2], int index); void *userData; ViewContext vc; + const bool *hide_vert; eV3DProjTest clip_flag; } foreachScreenObjectVert_userData; @@ -262,18 +263,19 @@ static void meshobject_foreachScreenVert__mapFunc(void *userData, const float UNUSED(no[3])) { foreachScreenObjectVert_userData *data = userData; + if (data->hide_vert && data->hide_vert[index]) { + return; + } struct MVert *mv = &((Mesh *)(data->vc.obact->data))->mvert[index]; - if (!(mv->flag & ME_HIDE)) { - float screen_co[2]; - - if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) != - V3D_PROJ_RET_OK) { - return; - } + float screen_co[2]; - data->func(data->userData, mv, screen_co, index); + if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) != + V3D_PROJ_RET_OK) { + return; } + + data->func(data->userData, mv, screen_co, index); } void meshobject_foreachScreenVert( @@ -297,6 +299,8 @@ void meshobject_foreachScreenVert( data.func = func; data.userData = userData; data.clip_flag = clip_flag; + data.hide_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert"); if (clip_flag & V3D_PROJ_TEST_CLIP_BB) { ED_view3d_clipping_local(vc->rv3d, vc->obact->obmat); diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index 88e004aac48..f50e933fdac 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -495,6 +495,8 @@ static void axis_set_view(bContext *C, .camera_old = v3d->camera, .ofs = rv3d->ofs, .quat = quat, + /* No undo because this switches to/from camera. */ + .undo_str = NULL, }); } else if (orig_persp == RV3D_CAMOB && v3d->camera) { @@ -518,6 +520,8 @@ static void axis_set_view(bContext *C, .ofs = ofs, .quat = quat, .dist = &dist, + /* No undo because this switches to/from camera. */ + .undo_str = NULL, }); } else { @@ -540,6 +544,8 @@ static void axis_set_view(bContext *C, &(const V3D_SmoothParams){ .quat = quat, .dyn_ofs = dyn_ofs_pt, + /* No undo because this isn't a camera view. */ + .undo_str = NULL, }); } } @@ -694,6 +700,8 @@ static void view3d_from_minmax(bContext *C, .camera_old = v3d->camera, .ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL, + /* The caller needs to use undo begin/end calls. */ + .undo_str = NULL, }); } else { @@ -704,6 +712,8 @@ static void view3d_from_minmax(bContext *C, &(const V3D_SmoothParams){ .ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL, + /* The caller needs to use undo begin/end calls. */ + .undo_str = NULL, }); } @@ -736,6 +746,7 @@ static void view3d_from_minmax_multi(bContext *C, static int view3d_all_exec(bContext *C, wmOperator *op) { + ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); @@ -802,6 +813,7 @@ static int view3d_all_exec(bContext *C, wmOperator *op) /* This is an approximation, see function documentation for details. */ ED_view3d_clipping_clamp_minmax(rv3d, min, max); } + ED_view3d_smooth_view_undo_begin(C, area); if (use_all_regions) { view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx); @@ -810,6 +822,8 @@ static int view3d_all_exec(bContext *C, wmOperator *op) view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx); } + ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); + return OPERATOR_FINISHED; } @@ -842,6 +856,7 @@ void VIEW3D_OT_view_all(wmOperatorType *ot) static int viewselected_exec(bContext *C, wmOperator *op) { + ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); @@ -971,6 +986,8 @@ static int viewselected_exec(bContext *C, wmOperator *op) ED_view3d_clipping_clamp_minmax(rv3d, min, max); } + ED_view3d_smooth_view_undo_begin(C, area); + if (use_all_regions) { view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx); } @@ -978,6 +995,8 @@ static int viewselected_exec(bContext *C, wmOperator *op) view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); } + ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); + return OPERATOR_FINISHED; } @@ -1020,8 +1039,14 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op) /* non camera center */ float new_ofs[3]; negate_v3_v3(new_ofs, scene->cursor.location); - ED_view3d_smooth_view( - C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs}); + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .ofs = new_ofs, + .undo_str = op->type->name, + }); /* Smooth view does view-lock #RV3D_BOXVIEW copy. */ } @@ -1074,8 +1099,14 @@ static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *ev ED_view3d_win_to_3d_int(v3d, region, new_ofs, event->mval, new_ofs); } negate_v3(new_ofs); - ED_view3d_smooth_view( - C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs}); + ED_view3d_smooth_view(C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .ofs = new_ofs, + .undo_str = op->type->name, + }); } return OPERATOR_FINISHED; @@ -1318,17 +1349,20 @@ static int view_camera_exec(bContext *C, wmOperator *op) /* finally do snazzy view zooming */ rv3d->persp = RV3D_CAMOB; - ED_view3d_smooth_view(C, - v3d, - region, - smooth_viewtx, - &(const V3D_SmoothParams){ - .camera = v3d->camera, - .ofs = rv3d->ofs, - .quat = rv3d->viewquat, - .dist = &rv3d->dist, - .lens = &v3d->lens, - }); + ED_view3d_smooth_view( + C, + v3d, + region, + smooth_viewtx, + &(const V3D_SmoothParams){ + .camera = v3d->camera, + .ofs = rv3d->ofs, + .quat = rv3d->viewquat, + .dist = &rv3d->dist, + .lens = &v3d->lens, + /* No undo because this changes cameras (and wont move the camera). */ + .undo_str = NULL, + }); } else { /* return to settings of last view */ @@ -1417,7 +1451,12 @@ static int vieworbit_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_force_finish(C, v3d, region); if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0 || (view_opposite != RV3D_VIEW_USER)) { - if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { + const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d); + if ((rv3d->persp != RV3D_CAMOB) || is_camera_lock) { + if (is_camera_lock) { + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_camera_lock_init(depsgraph, v3d, rv3d); + } int smooth_viewtx = WM_operator_smooth_viewtx_get(op); float quat_mul[4]; float quat_new[4]; @@ -1475,6 +1514,9 @@ static int vieworbit_exec(bContext *C, wmOperator *op) &(const V3D_SmoothParams){ .quat = quat_new, .dyn_ofs = dyn_ofs_pt, + /* Group as successive orbit may run by holding a key. */ + .undo_str = op->type->name, + .undo_grouped = true, }); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h index fc7bc11295a..925acd90573 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.h +++ b/source/blender/editors/space_view3d/view3d_navigate.h @@ -231,6 +231,14 @@ typedef struct V3D_SmoothParams { /** Alternate rotation center, when set `ofs` must be NULL. */ const float *dyn_ofs; + + /** When non-NULL, perform undo pushes when transforming the camera. */ + const char *undo_str; + /** + * When true use grouped undo pushes, use for incremental viewport manipulation + * which are likely to be activated by holding a key or from the mouse-wheel. + */ + bool undo_grouped; } V3D_SmoothParams; /** @@ -252,6 +260,22 @@ void ED_view3d_smooth_view(struct bContext *C, const V3D_SmoothParams *sview); /** + * Call before multiple smooth-view operations begin to properly handle undo. + * + * \note Only use explicit undo calls when multiple calls to smooth-view are necessary + * or when calling #ED_view3d_smooth_view_ex. + * Otherwise pass in #V3D_SmoothParams.undo_str so an undo step is pushed as needed. + */ +void ED_view3d_smooth_view_undo_begin(struct bContext *C, const struct ScrArea *area); +/** + * Run after multiple smooth-view operations have run to push undo as needed. + */ +void ED_view3d_smooth_view_undo_end(struct bContext *C, + const struct ScrArea *area, + const char *undo_str, + bool undo_grouped); + +/** * Apply the smooth-view immediately, use when we need to start a new view operation. * (so we don't end up half-applying a view operation when pressing keys quickly). */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c index 087ca72211e..af93aa50238 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_roll.c +++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c @@ -15,6 +15,8 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "DEG_depsgraph_query.h" + #include "ED_screen.h" #include "view3d_intern.h" @@ -167,7 +169,13 @@ static int viewroll_exec(bContext *C, wmOperator *op) } rv3d = region->regiondata; - if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { + + const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d); + if ((rv3d->persp != RV3D_CAMOB) || is_camera_lock) { + if (is_camera_lock) { + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_camera_lock_init(depsgraph, v3d, rv3d); + } ED_view3d_smooth_view_force_finish(C, v3d, region); @@ -202,6 +210,9 @@ static int viewroll_exec(bContext *C, wmOperator *op) &(const V3D_SmoothParams){ .quat = quat_new, .dyn_ofs = dyn_ofs_pt, + /* Group as successive roll may run by holding a key. */ + .undo_str = op->type->name, + .undo_grouped = true, }); viewops_data_free(C, op->customdata); diff --git a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c index 48af126d8a9..6b150d1e771 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c +++ b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c @@ -8,6 +8,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BKE_context.h" @@ -21,6 +22,115 @@ #include "view3d_intern.h" #include "view3d_navigate.h" /* own include */ +static void view3d_smoothview_apply_with_interp( + bContext *C, View3D *v3d, RegionView3D *rv3d, const bool use_autokey, const float factor); + +/* -------------------------------------------------------------------- */ +/** \name Smooth View Undo Handling + * + * When the camera is locked to the viewport smooth-view operations + * may need to perform an undo push. + * + * In this case the smooth-view camera transformation is temporarily completed, + * undo is pushed then the change is rewound, and smooth-view completes from it's timer. + * In the case smooth-view executed the change immediately - an undo push is called. + * + * NOTE(@campbellbarton): While this is not ideal it's necessary as making the undo-push + * once smooth-view is complete because smooth-view is non-blocking and it's possible other + * operations are executed once smooth-view has started. + * \{ */ + +void ED_view3d_smooth_view_undo_begin(bContext *C, const ScrArea *area) +{ + const View3D *v3d = area->spacedata.first; + Object *camera = v3d->camera; + if (!camera) { + return; + } + + /* Tag the camera object so it's known smooth-view is applied to the view-ports camera + * (needed to detect when a locked camera is being manipulated). + * NOTE: It doesn't matter if the actual object being manipulated is the camera or not. */ + camera->id.tag &= ~LIB_TAG_DOIT; + + LISTBASE_FOREACH (const ARegion *, region, &area->regionbase) { + if (region->regiontype != RGN_TYPE_WINDOW) { + continue; + } + const RegionView3D *rv3d = region->regiondata; + if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) { + camera->id.tag |= LIB_TAG_DOIT; + break; + } + } +} + +void ED_view3d_smooth_view_undo_end(bContext *C, + const ScrArea *area, + const char *undo_str, + const bool undo_grouped) +{ + View3D *v3d = area->spacedata.first; + Object *camera = v3d->camera; + if (!camera) { + return; + } + if (camera->id.tag & LIB_TAG_DOIT) { + /* Smooth view didn't touch the camera. */ + camera->id.tag &= ~LIB_TAG_DOIT; + return; + } + + if ((U.uiflag & USER_GLOBALUNDO) == 0) { + return; + } + + /* NOTE(@campbellbarton): It is not possible that a single viewport references different cameras + * so even in the case there is a quad-view with multiple camera views set, these will all + * reference the same camera. In this case it doesn't matter which region is used. + * If in the future multiple cameras are supported, this logic can be extended. */ + const ARegion *region_camera = NULL; + + /* An undo push should be performed. */ + bool is_interactive = false; + LISTBASE_FOREACH (const ARegion *, region, &area->regionbase) { + if (region->regiontype != RGN_TYPE_WINDOW) { + continue; + } + const RegionView3D *rv3d = region->regiondata; + if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) { + region_camera = region; + if (rv3d->sms) { + is_interactive = true; + } + } + } + + if (region_camera == NULL) { + return; + } + + RegionView3D *rv3d = region_camera->regiondata; + + /* Fast forward, undo push, then rewind. */ + if (is_interactive) { + view3d_smoothview_apply_with_interp(C, v3d, rv3d, false, 1.0f); + } + + if (undo_grouped) { + ED_view3d_camera_lock_undo_grouped_push(undo_str, v3d, rv3d, C); + } + else { + ED_view3d_camera_lock_undo_push(undo_str, v3d, rv3d, C); + } + + if (is_interactive) { + view3d_smoothview_apply_with_interp(C, v3d, rv3d, false, 0.0f); + } +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Smooth View Operator & Utilities * @@ -86,6 +196,11 @@ void ED_view3d_smooth_view_ex( const int smooth_viewtx, const V3D_SmoothParams *sview) { + /* In this case use #ED_view3d_smooth_view_undo_begin & end functions + * instead of passing in undo. */ + BLI_assert_msg(sview->undo_str == NULL, + "Only the 'ED_view3d_smooth_view' version of this function handles undo!"); + RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore sms = {{0}}; @@ -236,6 +351,13 @@ void ED_view3d_smooth_view_ex( WM_event_add_mousemove(win); } + + if (sms.to_camera == false) { + /* See comments in #ED_view3d_smooth_view_undo_begin for why this is needed. */ + if (v3d->camera) { + v3d->camera->id.tag &= ~LIB_TAG_DOIT; + } + } } void ED_view3d_smooth_view(bContext *C, @@ -249,97 +371,129 @@ void ED_view3d_smooth_view(bContext *C, wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); - ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview); + /* #ED_view3d_smooth_view_ex asserts this is not set as it doesn't support undo. */ + struct V3D_SmoothParams sview_no_undo = *sview; + sview_no_undo.undo_str = NULL; + sview_no_undo.undo_grouped = false; + + const bool do_undo = (sview->undo_str != NULL); + if (do_undo) { + ED_view3d_smooth_view_undo_begin(C, area); + } + + ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, &sview_no_undo); + + if (do_undo) { + ED_view3d_smooth_view_undo_end(C, area, sview->undo_str, sview->undo_grouped); + } } -/* only meant for timer usage */ -static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview) +/** + * Apply with interpolation, on completion run #view3d_smoothview_apply_and_finish. + */ +static void view3d_smoothview_apply_with_interp( + bContext *C, View3D *v3d, RegionView3D *rv3d, const bool use_autokey, const float factor) { - wmWindowManager *wm = CTX_wm_manager(C); - RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore *sms = rv3d->sms; - float step, step_inv; - if (sms->time_allowed != 0.0) { - step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed); + interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, factor); + + if (sms->use_dyn_ofs) { + view3d_orbit_apply_dyn_ofs( + rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs); } else { - step = 1.0f; + interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, factor); } - /* end timer */ - if (step >= 1.0f) { - wmWindow *win = CTX_wm_window(C); + rv3d->dist = interpf(sms->dst.dist, sms->src.dist, factor); + v3d->lens = interpf(sms->dst.lens, sms->src.lens, factor); - /* if we went to camera, store the original */ - if (sms->to_camera) { - rv3d->persp = RV3D_CAMOB; - view3d_smooth_view_state_restore(&sms->org, v3d, rv3d); - } - else { - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - - view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d); - - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + if (ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d)) { + if (use_autokey) { ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); } + } +} - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { - rv3d->view = sms->org_view; - } - - MEM_freeN(rv3d->sms); - rv3d->sms = NULL; +/** + * Apply the view-port transformation & free smooth-view related data. + */ +static void view3d_smoothview_apply_and_finish(bContext *C, View3D *v3d, RegionView3D *rv3d) +{ + wmWindowManager *wm = CTX_wm_manager(C); + struct SmoothView3DStore *sms = rv3d->sms; - WM_event_remove_timer(wm, win, rv3d->smooth_timer); - rv3d->smooth_timer = NULL; - rv3d->rflag &= ~RV3D_NAVIGATING; + wmWindow *win = CTX_wm_window(C); - /* Event handling won't know if a UI item has been moved under the pointer. */ - WM_event_add_mousemove(win); + /* if we went to camera, store the original */ + if (sms->to_camera) { + rv3d->persp = RV3D_CAMOB; + view3d_smooth_view_state_restore(&sms->org, v3d, rv3d); } else { - /* ease in/out */ - step = (3.0f * step * step - 2.0f * step * step * step); - - step_inv = 1.0f - step; - - interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step); - - if (sms->use_dyn_ofs) { - view3d_orbit_apply_dyn_ofs( - rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs); - } - else { - interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step); - } + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv; - v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv; + view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d); - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - if (ED_screen_animation_playing(wm)) { + if (ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d)) { ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); } } - if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) { - view3d_boxview_copy(CTX_wm_area(C), region); + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { + rv3d->view = sms->org_view; } + MEM_freeN(rv3d->sms); + rv3d->sms = NULL; + + WM_event_remove_timer(wm, win, rv3d->smooth_timer); + rv3d->smooth_timer = NULL; + rv3d->rflag &= ~RV3D_NAVIGATING; + + /* Event handling won't know if a UI item has been moved under the pointer. */ + WM_event_add_mousemove(win); + /* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636, * when switching camera in quad-view the other ortho views would zoom & reset. * * For now only redraw all regions when smooth-view finishes. */ - if (step >= 1.0f) { - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); +} + +/* only meant for timer usage */ + +static void view3d_smoothview_apply_from_timer(bContext *C, View3D *v3d, ARegion *region) +{ + wmWindowManager *wm = CTX_wm_manager(C); + RegionView3D *rv3d = region->regiondata; + struct SmoothView3DStore *sms = rv3d->sms; + float factor; + + if (sms->time_allowed != 0.0) { + factor = (float)((rv3d->smooth_timer->duration) / sms->time_allowed); } else { - ED_region_tag_redraw(region); + factor = 1.0f; + } + if (factor >= 1.0f) { + view3d_smoothview_apply_and_finish(C, v3d, rv3d); } + else { + /* Ease in/out smoothing. */ + factor = (3.0f * factor * factor - 2.0f * factor * factor * factor); + const bool use_autokey = ED_screen_animation_playing(wm); + view3d_smoothview_apply_with_interp(C, v3d, rv3d, use_autokey, factor); + } + + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_copy(CTX_wm_area(C), region); + } + + ED_region_tag_redraw(region); } static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) @@ -353,7 +507,7 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w return OPERATOR_PASS_THROUGH; } - view3d_smoothview_apply(C, v3d, region, true); + view3d_smoothview_apply_from_timer(C, v3d, region); return OPERATOR_FINISHED; } @@ -361,12 +515,10 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region) { RegionView3D *rv3d = region->regiondata; - if (rv3d && rv3d->sms) { - rv3d->sms->time_allowed = 0.0; /* force finishing */ - view3d_smoothview_apply(C, v3d, region, false); + view3d_smoothview_apply_and_finish(C, v3d, rv3d); - /* force update of view matrix so tools that run immediately after + /* Force update of view matrix so tools that run immediately after * can use them without redrawing first */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c index f834efe4a7b..7cafc3dfd42 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c @@ -159,11 +159,15 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) /* clamp after because we may have been zooming out */ CLAMP(new_dist, dist_range[0], dist_range[1]); - /* TODO(campbell): 'is_camera_lock' not currently working well. */ const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d); - if ((rv3d->persp == RV3D_CAMOB) && (is_camera_lock == false)) { + if (rv3d->persp == RV3D_CAMOB) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP); + if (is_camera_lock) { + ED_view3d_camera_lock_init(depsgraph, v3d, rv3d); + } + else { + ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP); + } } ED_view3d_smooth_view(C, @@ -173,6 +177,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) &(const V3D_SmoothParams){ .ofs = new_ofs, .dist = &new_dist, + .undo_str = op->type->name, }); if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 8eff9ee472f..763848574ed 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -335,14 +335,16 @@ static bool edbm_backbuf_check_and_select_verts_obmode(Mesh *me, const eSelectOp sel_op) { MVert *mv = me->mvert; - uint index; bool changed = false; const BLI_bitmap *select_bitmap = esel->select_bitmap; if (mv) { - for (index = 0; index < me->totvert; index++, mv++) { - if (!(mv->flag & ME_HIDE)) { + const bool *hide_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert"); + + for (int index = 0; index < me->totvert; index++, mv++) { + if (!(hide_vert && hide_vert[index])) { const bool is_select = mv->flag & SELECT; const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); @@ -362,14 +364,16 @@ static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me, const eSelectOp sel_op) { MPoly *mpoly = me->mpoly; - uint index; bool changed = false; const BLI_bitmap *select_bitmap = esel->select_bitmap; if (mpoly) { - for (index = 0; index < me->totpoly; index++, mpoly++) { - if (!(mpoly->flag & ME_HIDE)) { + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &me->pdata, CD_PROP_BOOL, ".hide_poly"); + + for (int index = 0; index < me->totpoly; index++, mpoly++) { + if (!(hide_poly && hide_poly[index])) { const bool is_select = mpoly->flag & ME_FACE_SEL; const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); @@ -1260,7 +1264,7 @@ static bool do_lasso_select_paintface(ViewContext *vc, } if (changed) { - paintface_flush_flags(vc->C, ob, SELECT); + paintface_flush_flags(vc->C, ob, true, false); } return changed; } @@ -2893,11 +2897,6 @@ static int view3d_select_exec(bContext *C, wmOperator *op) bool changed = false; int mval[2]; - RNA_int_get_array(op->ptr, "location", mval); - - view3d_operator_needs_opengl(C); - BKE_object_update_select_id(CTX_data_main(C)); - if (object_only) { obedit = NULL; obact = NULL; @@ -2908,6 +2907,19 @@ static int view3d_select_exec(bContext *C, wmOperator *op) center = false; } + if (obedit && enumerate) { + /* Enumerate makes no sense in edit-mode unless also explicitly picking objects or bones. + * Pass the event through so the event may be handled by loop-select for e.g. see: T100204. */ + if (obedit->type != OB_ARMATURE) { + return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED; + } + } + + RNA_int_get_array(op->ptr, "location", mval); + + view3d_operator_needs_opengl(C); + BKE_object_update_select_id(CTX_data_main(C)); + if (obedit && object_only == false) { if (obedit->type == OB_MESH) { changed = EDBM_select_pick(C, mval, ¶ms); @@ -3184,7 +3196,7 @@ static bool do_paintface_box_select(ViewContext *vc, } if (changed) { - paintface_flush_flags(vc->C, vc->obact, SELECT); + paintface_flush_flags(vc->C, vc->obact, true, false); } return changed; } @@ -4085,7 +4097,7 @@ static bool paint_facesel_circle_select(ViewContext *vc, } if (changed) { - paintface_flush_flags(vc->C, ob, SELECT); + paintface_flush_flags(vc->C, ob, true, false); } return changed; } diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 99f8cbc975b..5f2a4e8c4cc 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -689,6 +689,18 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d, return false; } +bool ED_view3d_camera_lock_undo_test(const View3D *v3d, + const RegionView3D *rv3d, + struct bContext *C) +{ + if (ED_view3d_camera_lock_check(v3d, rv3d)) { + if (ED_undo_is_memfile_compatible(C)) { + return true; + } + } + return false; +} + /** * Create a MEMFILE undo-step for locked camera movement when transforming the view. * Edit and texture paint mode don't use MEMFILE undo so undo push is skipped for them. @@ -696,31 +708,35 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d, * unnecessary undo steps so undo push for them is not supported for now. Also operators that uses * smooth view for navigation are excluded too, but they can be supported, see: D15345. */ -static bool view3d_camera_lock_undo_ex( - const char *str, View3D *v3d, RegionView3D *rv3d, struct bContext *C, bool undo_group) -{ - if (ED_view3d_camera_lock_check(v3d, rv3d)) { - if (ED_undo_is_memfile_compatible(C)) { - if (undo_group) { - ED_undo_grouped_push(C, str); - } - else { - ED_undo_push(C, str); - } - return true; +static bool view3d_camera_lock_undo_ex(const char *str, + const View3D *v3d, + const RegionView3D *rv3d, + struct bContext *C, + const bool undo_group) +{ + if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) { + if (undo_group) { + ED_undo_grouped_push(C, str); + } + else { + ED_undo_push(C, str); } + return true; } return false; } -bool ED_view3d_camera_lock_undo_push(const char *str, View3D *v3d, RegionView3D *rv3d, bContext *C) +bool ED_view3d_camera_lock_undo_push(const char *str, + const View3D *v3d, + const RegionView3D *rv3d, + bContext *C) { return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, false); } bool ED_view3d_camera_lock_undo_grouped_push(const char *str, - View3D *v3d, - RegionView3D *rv3d, + const View3D *v3d, + const RegionView3D *rv3d, bContext *C) { return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, true); diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index fc88737ca70..b8042a9f215 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -202,6 +202,8 @@ static void sync_viewport_camera_smoothview(bContext *C, .quat = other_rv3d->viewquat, .dist = &other_rv3d->dist, .lens = &other_v3d->lens, + /* No undo because this switches cameras. */ + .undo_str = NULL, }); } else { @@ -256,6 +258,8 @@ static int view3d_setobjectascamera_exec(bContext *C, wmOperator *op) .quat = rv3d->viewquat, .dist = &rv3d->dist, .lens = &v3d->lens, + /* No undo because this switches cameras. */ + .undo_str = NULL, }); } @@ -939,6 +943,8 @@ static bool view3d_localview_init(const Depsgraph *depsgraph, .quat = rv3d->viewquat, .dist = ok_dist ? &dist_new : NULL, .lens = &v3d->lens, + /* No undo because this doesn't move the camera. */ + .undo_str = NULL, }); } } @@ -1008,6 +1014,8 @@ static void view3d_localview_exit(const Depsgraph *depsgraph, .ofs = rv3d->localvd->ofs, .quat = rv3d->localvd->viewquat, .dist = &rv3d->localvd->dist, + /* No undo because this doesn't move the camera. */ + .undo_str = NULL, }); } diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index b5a85f57b84..f67a44703e5 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1314,7 +1314,8 @@ void transform_convert_mesh_crazyspace_detect(TransInfo *t, * correction with \a quats, relative to the coordinates after * the modifiers that support deform matrices \a defcos. */ -#if 0 /* TODO(campbell): fix crazy-space & extrude so it can be enabled for general use. */ +#if 0 /* TODO(@campbellbarton): fix crazy-space & extrude so it can be enabled for general use. \ + */ if ((totleft > 0) || (totleft == -1)) #else if (totleft > 0) @@ -2123,7 +2124,7 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { /* table needs to be created for each edit command, since vertices can move etc */ ED_mesh_mirror_spatial_table_end(tc->obedit); - /* TODO(campbell): xform: We need support for many mirror objects at once! */ + /* TODO(@campbellbarton): xform: We need support for many mirror objects at once! */ break; } } diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index d95bc7b976f..f3bef2c283b 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -270,7 +270,7 @@ static void createTransUVs(bContext *C, TransInfo *t) continue; } - island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__); + island_center = MEM_callocN(sizeof(*island_center) * elementmap->total_islands, __func__); } BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { @@ -315,9 +315,7 @@ static void createTransUVs(bContext *C, TransInfo *t) } if (is_island_center) { - int i; - - for (i = 0; i < elementmap->totalIslands; i++) { + for (int i = 0; i < elementmap->total_islands; i++) { mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num); mul_v2_v2(island_center[i].co, t->aspect); } diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index 131a7fd517f..a3b5fd2c575 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -261,7 +261,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup) copy_m3_m3(ggd->data.normal_mat3, tbounds_normal.axis); } - /* TODO(campbell): run second since this modifies the 3D view, it should not. */ + /* TODO(@campbellbarton): run second since this modifies the 3D view, it should not. */ if (!ED_transform_calc_gizmo_stats(C, &(struct TransformCalcParams){ .orientation_index = ggd->data.orientation_index + 1, diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c index acc6b20810f..a48f84ef0bc 100644 --- a/source/blender/editors/transform/transform_mode_bend.c +++ b/source/blender/editors/transform/transform_mode_bend.c @@ -262,7 +262,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) +values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle)); } - /* TODO(campbell): xform, compensate object center. */ + /* TODO(@campbellbarton): xform, compensate object center. */ FOREACH_TRANS_DATA_CONTAINER (t, tc) { float warp_sta_local[3]; diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index a3c49d2362f..b48ba0640ad 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -1204,7 +1204,7 @@ void drawEdgeSlide(TransInfo *t) immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); immBegin(GPU_PRIM_LINES, sld->totsv * 2); - /* TODO(campbell): Loop over all verts. */ + /* TODO(@campbellbarton): Loop over all verts. */ sv = sld->sv; for (i = 0; i < sld->totsv; i++, sv++) { float a[3], b[3]; diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index ce14e6b180f..bb24bdac690 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -269,7 +269,7 @@ static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportLis CLOG_INFO(&LOG, 1, "direction=%s", (step == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO"); - /* TODO(campbell): undo_system: use undo system */ + /* TODO(@campbellbarton): undo_system: use undo system */ /* grease pencil can be can be used in plenty of spaces, so check it first */ /* FIXME: This gpencil undo effectively only supports the one step undo/redo, undo based on name * or index is fully not implemented. diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 3cfe6bd61a4..36a881ec158 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -176,7 +176,7 @@ void ED_editors_init(bContext *C) } } else { - /* TODO(campbell): avoid operator calls. */ + /* TODO(@campbellbarton): avoid operator calls. */ if (obact == ob) { ED_object_mode_set(C, mode); } diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index fc5fb9f9c28..1ebbb0cecc3 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -431,7 +431,7 @@ void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info) immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3fv(color); - /* TODO(campbell): lock to pixels. */ + /* TODO(@campbellbarton): lock to pixels. */ rctf sample_rect_fl; BLI_rctf_init_pt_radius( &sample_rect_fl, diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 092f0c49d8a..6755630d3ef 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -541,14 +541,11 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool) } bool changed = false; - - /* Loop backwards to simplify logic. */ - int j1 = element_map->total_uvs; - for (int i = element_map->totalIslands - 1; i >= 0; --i) { - int j0 = element_map->islandIndices[i]; - changed |= uvedit_uv_straighten_elements( - element_map->storage + j0, j1 - j0, cd_loop_uv_offset, tool); - j1 = j0; + for (int i = 0; i < element_map->total_islands; i++) { + changed |= uvedit_uv_straighten_elements(element_map->storage + element_map->island_indices[i], + element_map->island_total_uvs[i], + cd_loop_uv_offset, + tool); } BM_uv_element_map_free(element_map); diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index b1ed00e2c57..d88da21ef98 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1422,7 +1422,7 @@ static BMLoop *bm_select_edgeloop_single_side_next(const Scene *scene, scene, l_step, v_from_next, cd_loop_uv_offset); } -/* TODO(campbell): support this in the BMesh API, as we have for clearing other types. */ +/* TODO(@campbellbarton): support this in the BMesh API, as we have for clearing other types. */ static void bm_loop_tags_clear(BMesh *bm) { BMIter iter; @@ -5395,7 +5395,7 @@ static void uv_isolate_selected_islands(const Scene *scene, return; } - int num_islands = elementmap->totalIslands; + int num_islands = elementmap->total_islands; /* Boolean array that tells if island with index i is completely selected or not. */ bool *is_island_not_selected = MEM_callocN(sizeof(bool) * (num_islands), __func__); diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 26ed98ba236..4a2ea5c3aa6 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -287,14 +287,6 @@ static void stitch_update_header(StitchStateContainer *ssc, bContext *C) } } -static int getNumOfIslandUvs(UvElementMap *elementMap, int island) -{ - if (island == elementMap->totalIslands - 1) { - return elementMap->total_uvs - elementMap->islandIndices[island]; - } - return elementMap->islandIndices[island + 1] - elementMap->islandIndices[island]; -} - static void stitch_uv_rotate(const float mat[2][2], const float medianPoint[2], float uv[2], @@ -419,10 +411,9 @@ static void stitch_calculate_island_snapping(StitchState *state, int final) { BMesh *bm = state->em->bm; - int i; UvElement *element; - for (i = 0; i < state->element_map->totalIslands; i++) { + for (int i = 0; i < state->element_map->total_islands; i++) { if (island_stitch_data[i].addedForPreview) { int numOfIslandUVs = 0, j; int totelem = island_stitch_data[i].num_rot_elements_neg + @@ -464,8 +455,8 @@ static void stitch_calculate_island_snapping(StitchState *state, } angle_to_mat2(rotation_mat, rotation); - numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); - element = &state->element_map->storage[state->element_map->islandIndices[i]]; + numOfIslandUVs = state->element_map->island_total_uvs[i]; + element = &state->element_map->storage[state->element_map->island_indices[i]]; for (j = 0; j < numOfIslandUVs; j++, element++) { /* stitchable uvs have already been processed, don't process */ if (!(element->flag & STITCH_PROCESSED)) { @@ -984,7 +975,7 @@ static int stitch_process_data(StitchStateContainer *ssc, preview_position[i].data_position = STITCH_NO_PREVIEW; } - island_stitch_data = MEM_callocN(sizeof(*island_stitch_data) * state->element_map->totalIslands, + island_stitch_data = MEM_callocN(sizeof(*island_stitch_data) * state->element_map->total_islands, "stitch_island_data"); if (!island_stitch_data) { return 0; @@ -1009,7 +1000,7 @@ static int stitch_process_data(StitchStateContainer *ssc, } /* Remember stitchable candidates as places the 'I' button will stop at. */ - for (int island_idx = 0; island_idx < state->element_map->totalIslands; island_idx++) { + for (int island_idx = 0; island_idx < state->element_map->total_islands; island_idx++) { state->island_is_stitchable[island_idx] = island_stitch_data[island_idx].stitchableCandidate ? true : false; @@ -1017,10 +1008,10 @@ static int stitch_process_data(StitchStateContainer *ssc, if (is_active_state) { /* set static island to one that is added for preview */ - ssc->static_island %= state->element_map->totalIslands; + ssc->static_island %= state->element_map->total_islands; while (!(island_stitch_data[ssc->static_island].stitchableCandidate)) { ssc->static_island++; - ssc->static_island %= state->element_map->totalIslands; + ssc->static_island %= state->element_map->total_islands; /* this is entirely possible if for example limit stitching * with no stitchable verts or no selection */ if (ssc->static_island == previous_island) { @@ -1141,13 +1132,11 @@ static int stitch_process_data(StitchStateContainer *ssc, * Setup preview for stitchable islands * ****************************************/ if (ssc->snap_islands) { - for (i = 0; i < state->element_map->totalIslands; i++) { + for (i = 0; i < state->element_map->total_islands; i++) { if (island_stitch_data[i].addedForPreview) { - int numOfIslandUVs = 0, j; - UvElement *element; - numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); - element = &state->element_map->storage[state->element_map->islandIndices[i]]; - for (j = 0; j < numOfIslandUVs; j++, element++) { + int numOfIslandUVs = state->element_map->island_total_uvs[i]; + UvElement *element = &state->element_map->storage[state->element_map->island_indices[i]]; + for (int j = 0; j < numOfIslandUVs; j++, element++) { stitch_set_face_preview_buffer_position(element->l->f, preview, preview_position); } } @@ -2120,8 +2109,8 @@ static StitchState *stitch_init(bContext *C, /***** initialize static island preview data *****/ state->tris_per_island = MEM_mallocN( - sizeof(*state->tris_per_island) * state->element_map->totalIslands, "stitch island tris"); - for (i = 0; i < state->element_map->totalIslands; i++) { + sizeof(*state->tris_per_island) * state->element_map->total_islands, "stitch island tris"); + for (i = 0; i < state->element_map->total_islands; i++) { state->tris_per_island[i] = 0; } @@ -2133,7 +2122,7 @@ static StitchState *stitch_init(bContext *C, } } - state->island_is_stitchable = MEM_callocN(sizeof(bool) * state->element_map->totalIslands, + state->island_is_stitchable = MEM_callocN(sizeof(bool) * state->element_map->total_islands, "stitch I stops"); if (!state->island_is_stitchable) { state_delete(state); @@ -2157,7 +2146,7 @@ static bool goto_next_island(StitchStateContainer *ssc) do { ssc->static_island++; - if (ssc->static_island >= active_state->element_map->totalIslands) { + if (ssc->static_island >= active_state->element_map->total_islands) { /* go to next object */ ssc->active_object_index++; ssc->active_object_index %= ssc->objects_len; @@ -2307,7 +2296,7 @@ static int stitch_init_all(bContext *C, wmOperator *op) ssc->static_island = RNA_int_get(op->ptr, "static_island"); StitchState *state = ssc->states[ssc->active_object_index]; - ssc->static_island %= state->element_map->totalIslands; + ssc->static_island %= state->element_map->total_islands; /* If the initial active object doesn't have any stitchable islands * then no active island will be seen in the UI. diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h index 5285aefbd4c..ff110f18ffb 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.h +++ b/source/blender/geometry/GEO_uv_parametrizer.h @@ -13,8 +13,8 @@ extern "C" { #endif typedef struct ParamHandle ParamHandle; /* Handle to an array of charts. */ -typedef intptr_t ParamKey; /* Key (hash) for identifying verts and faces. */ -#define PARAM_KEY_MAX INTPTR_MAX +typedef uintptr_t ParamKey; /* Key (hash) for identifying verts and faces. */ +#define PARAM_KEY_MAX UINTPTR_MAX /* -------------------------------------------------------------------- */ /** \name Chart Construction: diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index d61941aa071..e252e28805e 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -154,7 +154,7 @@ static void gather_point_attributes_to_interpolate(const CurveComponent &src_com retrieve_attribute_spans( ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); - /* Attributes that aren't interpolated like Bezier handles still have to be be copied + /* Attributes that aren't interpolated like Bezier handles still have to be copied * to the result when there are any unselected curves of the corresponding type. */ retrieve_attribute_spans(ids_no_interpolation, src_component, diff --git a/source/blender/geometry/intern/uv_parametrizer.cc b/source/blender/geometry/intern/uv_parametrizer.cc index ae2794bf9bc..b7526d82ecc 100644 --- a/source/blender/geometry/intern/uv_parametrizer.cc +++ b/source/blender/geometry/intern/uv_parametrizer.cc @@ -30,7 +30,7 @@ /* Special Purpose Hash */ -typedef intptr_t PHashKey; +typedef uintptr_t PHashKey; typedef struct PHashLink { struct PHashLink *next; @@ -45,7 +45,7 @@ typedef struct PHash { /* Simplices */ -typedef struct PVert { +struct PVert { struct PVert *nextlink; union PVertUnion { @@ -58,9 +58,9 @@ typedef struct PVert { float co[3]; float uv[2]; uint flag; -} PVert; +}; -typedef struct PEdge { +struct PEdge { struct PEdge *nextlink; union PEdgeUnion { @@ -76,9 +76,9 @@ typedef struct PEdge { struct PFace *face; float *orig_uv, old_uv[2]; uint flag; -} PEdge; +}; -typedef struct PFace { +struct PFace { struct PFace *nextlink; union PFaceUnion { @@ -89,8 +89,8 @@ typedef struct PFace { } u; struct PEdge *edge; - uchar flag; -} PFace; + uint flag; +}; enum PVertFlag { PVERT_PIN = 1, @@ -123,7 +123,7 @@ enum PFaceFlag { /* Chart */ -typedef struct PChart { +struct PChart { PVert *verts; PEdge *edges; PFace *faces; @@ -151,7 +151,7 @@ typedef struct PChart { } u; bool has_pins; -} PChart; +}; enum PHandleState { PHANDLE_STATE_ALLOCATED, @@ -160,7 +160,7 @@ enum PHandleState { PHANDLE_STATE_STRETCH, }; -typedef struct ParamHandle { +struct ParamHandle { enum PHandleState state; MemArena *arena; MemArena *polyfill_arena; @@ -181,7 +181,7 @@ typedef struct ParamHandle { RNG *rng; float blend; -} ParamHandle; +}; /* PHash * - special purpose hash that keeps all its elements in a single linked list. @@ -219,8 +219,8 @@ static void phash_delete(PHash *ph) { if (ph) { MEM_SAFE_FREE(ph->buckets); + MEM_freeN(ph); } - MEM_SAFE_FREE(ph); } static int phash_size(PHash *ph) @@ -234,7 +234,7 @@ static void phash_insert(PHash *ph, PHashLink *link) uintptr_t hash = PHASH_hash(ph, link->key); PHashLink *lookup = ph->buckets[hash]; - if (lookup == NULL) { + if (lookup == nullptr) { /* insert in front of the list */ ph->buckets[hash] = link; link->next = *(ph->list); @@ -249,13 +249,13 @@ static void phash_insert(PHash *ph, PHashLink *link) ph->size++; if (ph->size > (size * 3)) { - PHashLink *next = NULL, *first = *(ph->list); + PHashLink *next = nullptr, *first = *(ph->list); ph->cursize = PHashSizes[++ph->cursize_id]; MEM_freeN(ph->buckets); ph->buckets = (PHashLink **)MEM_callocN(ph->cursize * sizeof(*ph->buckets), "PHashBuckets"); ph->size = 0; - *(ph->list) = NULL; + *(ph->list) = nullptr; for (link = first; link; link = next) { next = link->next; @@ -274,7 +274,7 @@ static PHashLink *phash_lookup(PHash *ph, PHashKey key) return link; } if (PHASH_hash(ph, link->key) != hash) { - return NULL; + return nullptr; } } @@ -290,7 +290,7 @@ static PHashLink *phash_next(PHash *ph, PHashKey key, PHashLink *link) return link; } if (PHASH_hash(ph, link->key) != hash) { - return NULL; + return nullptr; } } @@ -456,7 +456,7 @@ static PEdge *p_wheel_edge_next(PEdge *e) static PEdge *p_wheel_edge_prev(PEdge *e) { - return (e->pair) ? e->pair->next : NULL; + return (e->pair) ? e->pair->next : nullptr; } static PEdge *p_boundary_edge_next(PEdge *e) @@ -694,7 +694,7 @@ static PEdge *p_edge_lookup(ParamHandle *handle, const PHashKey *vkeys) e = (PEdge *)phash_next(handle->hash_edges, key, (PHashLink *)e); } - return NULL; + return nullptr; } static int p_face_exists(ParamHandle *handle, const ParamKey *pvkeys, int i1, int i2, int i3) @@ -769,7 +769,7 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv key = PHASH_edge(key1, key2); pe = (PEdge *)phash_lookup(handle->hash_edges, key); - *r_pair = NULL; + *r_pair = nullptr; while (pe) { if (pe != e) { @@ -782,7 +782,7 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv /* don't connect seams and t-junctions */ if ((pe->flag & PEDGE_SEAM) || *r_pair || (topology_from_uvs && p_edge_implicit_seam(e, pe))) { - *r_pair = NULL; + *r_pair = nullptr; return false; } @@ -796,12 +796,12 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv if (*r_pair && (e->vert == (*r_pair)->vert)) { if ((*r_pair)->next->pair || (*r_pair)->next->next->pair) { /* non unfoldable, maybe mobius ring or klein bottle */ - *r_pair = NULL; + *r_pair = nullptr; return false; } } - return (*r_pair != NULL); + return (*r_pair != nullptr); } static bool p_edge_connect_pair(ParamHandle *handle, @@ -809,7 +809,7 @@ static bool p_edge_connect_pair(ParamHandle *handle, bool topology_from_uvs, PEdge ***stack) { - PEdge *pair = NULL; + PEdge *pair = nullptr; if (!e->pair && p_edge_has_pair(handle, e, topology_from_uvs, &pair)) { if (e->vert == pair->vert) { @@ -825,7 +825,7 @@ static bool p_edge_connect_pair(ParamHandle *handle, } } - return (e->pair != NULL); + return (e->pair != nullptr); } static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs) @@ -873,14 +873,14 @@ static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs) ncharts++; } - MEM_SAFE_FREE(stackbase); + MEM_freeN(stackbase); return ncharts; } static void p_split_vert(ParamHandle *handle, PChart *chart, PEdge *e) { - PEdge *we, *lastwe = NULL; + PEdge *we, *lastwe = nullptr; PVert *v = e->vert; bool copy = true; @@ -993,9 +993,9 @@ static PFace *p_face_add(ParamHandle *handle) e2->next = e3; e3->next = e1; - e1->pair = NULL; - e2->pair = NULL; - e3->pair = NULL; + e1->pair = nullptr; + e2->pair = nullptr; + e3->pair = nullptr; e1->flag = 0; e2->flag = 0; @@ -1073,7 +1073,7 @@ static PFace *p_face_add_fill(ParamHandle *handle, PChart *chart, PVert *v1, PVe e2->vert = v2; e3->vert = v3; - e1->orig_uv = e2->orig_uv = e3->orig_uv = NULL; + e1->orig_uv = e2->orig_uv = e3->orig_uv = nullptr; f->nextlink = chart->faces; chart->faces = f; @@ -1125,7 +1125,7 @@ static void p_chart_boundaries(PChart *chart, PEdge **r_outer) chart->nboundaries = 0; if (r_outer) { - *r_outer = NULL; + *r_outer = nullptr; } for (e = chart->edges; e; e = e->nextlink) { @@ -1216,7 +1216,7 @@ static void p_chart_fill_boundary(ParamHandle *handle, PChart *chart, PEdge *be, BLI_heap_remove(heap, e1->u.heaplink); BLI_heap_remove(heap, e2->u.heaplink); - e->u.heaplink = e1->u.heaplink = e2->u.heaplink = NULL; + e->u.heaplink = e1->u.heaplink = e2->u.heaplink = nullptr; e->flag |= PEDGE_FILLED; e1->flag |= PEDGE_FILLED; @@ -1254,7 +1254,7 @@ static void p_chart_fill_boundary(ParamHandle *handle, PChart *chart, PEdge *be, } } - BLI_heap_free(heap, NULL); + BLI_heap_free(heap, nullptr); } static void p_chart_fill_boundaries(ParamHandle *handle, PChart *chart, PEdge *outer) @@ -1537,7 +1537,7 @@ static void p_vert_harmonic_insert(PVert *v) e = p_wheel_edge_next(e); } while (e && (e != v->edge)); - if (e == NULL) { + if (e == nullptr) { npoints++; } @@ -1551,7 +1551,7 @@ static void p_vert_harmonic_insert(PVert *v) points[i][0] = e->next->vert->uv[0]; points[i][1] = e->next->vert->uv[1]; - if (nexte == NULL) { + if (nexte == nullptr) { i++; points[i][0] = e->next->next->vert->uv[0]; points[i][1] = e->next->next->vert->uv[1]; @@ -1895,8 +1895,8 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair) int nshapeold = 0, nshapenew = 0; p_collapsing_verts(edge, pair, &oldv, &keepv); - oldf1 = (edge) ? edge->face : NULL; - oldf2 = (pair) ? pair->face : NULL; + oldf1 = (edge) ? edge->face : nullptr; + oldf2 = (pair) ? pair->face : nullptr; sub_v3_v3v3(edgevec, keepv->co, oldv->co); @@ -1917,7 +1917,7 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair) # if 0 shapecost += dot_v3v3(co1, keepv->co); - if (p_wheel_edge_next(e) == NULL) { + if (p_wheel_edge_next(e) == nullptr) { shapecost += dot_v3v3(co2, keepv->co); } # endif @@ -1970,14 +1970,14 @@ static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine { PEdge *e, *enext, *pair; - *r_mine = NULL; + *r_mine = nullptr; *r_mincost = 0.0f; e = vert->edge; do { if (p_collapse_allowed(e, e->pair)) { float cost = p_collapse_cost(e, e->pair); - if ((*r_mine == NULL) || (cost < *r_mincost)) { + if ((*r_mine == nullptr) || (cost < *r_mincost)) { *r_mincost = cost; *r_mine = e; } @@ -1985,14 +1985,14 @@ static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine enext = p_wheel_edge_next(e); - if (enext == NULL) { + if (enext == nullptr) { /* The other boundary edge, where we only have the pair half-edge. */ pair = e->next->next; - if (p_collapse_allowed(NULL, pair)) { - float cost = p_collapse_cost(NULL, pair); + if (p_collapse_allowed(nullptr, pair)) { + float cost = p_collapse_cost(nullptr, pair); - if ((*r_mine == NULL) || (cost < *r_mincost)) { + if ((*r_mine == nullptr) || (cost < *r_mincost)) { *r_mincost = cost; *r_mine = pair; } @@ -2009,13 +2009,13 @@ static void p_chart_post_collapse_flush(PChart *chart, PEdge *collapsed) { /* Move to `collapsed_*`. */ - PVert *v, *nextv = NULL, *verts = chart->verts; - PEdge *e, *nexte = NULL, *edges = chart->edges, *laste = NULL; - PFace *f, *nextf = NULL, *faces = chart->faces; + PVert *v, *nextv = nullptr, *verts = chart->verts; + PEdge *e, *nexte = nullptr, *edges = chart->edges, *laste = nullptr; + PFace *f, *nextf = nullptr, *faces = chart->faces; - chart->verts = chart->collapsed_verts = NULL; - chart->edges = chart->collapsed_edges = NULL; - chart->faces = chart->collapsed_faces = NULL; + chart->verts = chart->collapsed_verts = nullptr; + chart->edges = chart->collapsed_edges = nullptr; + chart->faces = chart->collapsed_faces = nullptr; chart->nverts = chart->nedges = chart->nfaces = 0; @@ -2079,9 +2079,9 @@ static void p_chart_post_split_flush(PChart *chart) { /* Move from `collapsed_*`. */ - PVert *v, *nextv = NULL; - PEdge *e, *nexte = NULL; - PFace *f, *nextf = NULL; + PVert *v, *nextv = nullptr; + PEdge *e, *nexte = nullptr; + PFace *f, *nextf = nullptr; for (v = chart->collapsed_verts; v; v = nextv) { nextv = v->nextlink; @@ -2104,9 +2104,9 @@ static void p_chart_post_split_flush(PChart *chart) chart->nfaces++; } - chart->collapsed_verts = NULL; - chart->collapsed_edges = NULL; - chart->collapsed_faces = NULL; + chart->collapsed_verts = nullptr; + chart->collapsed_edges = nullptr; + chart->collapsed_faces = nullptr; } static void p_chart_simplify_compute(PChart *chart) @@ -2118,7 +2118,7 @@ static void p_chart_simplify_compute(PChart *chart) Heap *heap = BLI_heap_new(); PVert *v, **wheelverts; - PEdge *collapsededges = NULL, *e; + PEdge *collapsededges = nullptr, *e; int nwheelverts, i, ncollapsed = 0; wheelverts = MEM_mallocN(sizeof(PVert *) * chart->nverts, "PChartWheelVerts"); @@ -2126,7 +2126,7 @@ static void p_chart_simplify_compute(PChart *chart) /* insert all potential collapses into heap */ for (v = chart->verts; v; v = v->nextlink) { float cost; - PEdge *e = NULL; + PEdge *e = nullptr; p_collapse_cost_vertex(v, &cost, &e); @@ -2134,12 +2134,12 @@ static void p_chart_simplify_compute(PChart *chart) v->u.heaplink = BLI_heap_insert(heap, cost, e); } else { - v->u.heaplink = NULL; + v->u.heaplink = nullptr; } } for (e = chart->edges; e; e = e->nextlink) { - e->u.nextcollapse = NULL; + e->u.nextcollapse = nullptr; } /* pop edge collapse out of heap one by one */ @@ -2159,12 +2159,12 @@ static void p_chart_simplify_compute(PChart *chart) if (edge->vert->u.heaplink != link) { edge->flag |= (PEDGE_COLLAPSE_EDGE | PEDGE_COLLAPSE_PAIR); - edge->next->vert->u.heaplink = NULL; + edge->next->vert->u.heaplink = nullptr; SWAP(PEdge *, edge, pair); } else { edge->flag |= PEDGE_COLLAPSE_EDGE; - edge->vert->u.heaplink = NULL; + edge->vert->u.heaplink = nullptr; } p_collapsing_verts(edge, pair, &oldv, &keepv); @@ -2177,7 +2177,7 @@ static void p_chart_simplify_compute(PChart *chart) wheelverts[nwheelverts++] = wheele->next->vert; nexte = p_wheel_edge_next(wheele); - if (nexte == NULL) { + if (nexte == nullptr) { wheelverts[nwheelverts++] = wheele->next->next->vert; } @@ -2189,13 +2189,13 @@ static void p_chart_simplify_compute(PChart *chart) for (i = 0; i < nwheelverts; i++) { float cost; - PEdge *collapse = NULL; + PEdge *collapse = nullptr; v = wheelverts[i]; if (v->u.heaplink) { BLI_heap_remove(heap, v->u.heaplink); - v->u.heaplink = NULL; + v->u.heaplink = nullptr; } p_collapse_cost_vertex(v, &cost, &collapse); @@ -2209,7 +2209,7 @@ static void p_chart_simplify_compute(PChart *chart) } MEM_freeN(wheelverts); - BLI_heap_free(heap, NULL); + BLI_heap_free(heap, nullptr); p_chart_post_collapse_flush(chart, collapsededges); } @@ -2260,14 +2260,14 @@ static void p_chart_simplify(PChart *chart) #define ABF_MAX_ITER 20 -typedef struct PAbfSystem { +using PAbfSystem = struct PAbfSystem { int ninterior, nfaces, nangles; float *alpha, *beta, *sine, *cosine, *weight; float *bAlpha, *bTriangle, *bInterior; float *lambdaTriangle, *lambdaPlanar, *lambdaLength; float (*J2dt)[3], *bstar, *dstar; float minangle, maxangle; -} PAbfSystem; +}; static void p_abf_setup_system(PAbfSystem *sys) { @@ -2847,8 +2847,8 @@ static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2) static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVert **pin2) { - PEdge *be, *lastbe = NULL, *maxe1 = NULL, *maxe2 = NULL, *be1, *be2; - PEdge *cure = NULL, *firste1 = NULL, *firste2 = NULL, *nextbe; + PEdge *be, *lastbe = nullptr, *maxe1 = nullptr, *maxe2 = nullptr, *be1, *be2; + PEdge *cure = nullptr, *firste1 = nullptr, *firste2 = nullptr, *nextbe; float maxlen = 0.0f, curlen = 0.0f, totlen = 0.0f, firstlen = 0.0f; float len1, len2; @@ -2887,7 +2887,7 @@ static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVe } curlen = 0.0f; - cure = NULL; + cure = nullptr; } lastbe = be; @@ -2962,8 +2962,8 @@ static void p_chart_extrema_verts(PChart *chart, PVert **pin1, PVert **pin2) minv[0] = minv[1] = minv[2] = 1e20; maxv[0] = maxv[1] = maxv[2] = -1e20; - minvert[0] = minvert[1] = minvert[2] = NULL; - maxvert[0] = maxvert[1] = maxvert[2] = NULL; + minvert[0] = minvert[1] = minvert[2] = nullptr; + maxvert[0] = maxvert[1] = maxvert[2] = nullptr; for (v = chart->verts; v; v = v->nextlink) { for (i = 0; i < 3; i++) { @@ -3027,7 +3027,7 @@ static void p_chart_lscm_begin(PChart *chart, bool live, bool abf) } if ((live && (!select || !deselect))) { - chart->u.lscm.context = NULL; + chart->u.lscm.context = nullptr; } else { #if 0 @@ -3245,13 +3245,13 @@ static void p_chart_lscm_transform_single_pin(PChart *chart) static void p_chart_lscm_end(PChart *chart) { EIG_linear_solver_delete(chart->u.lscm.context); - chart->u.lscm.context = NULL; + chart->u.lscm.context = nullptr; MEM_SAFE_FREE(chart->u.lscm.abf_alpha); - chart->u.lscm.pin1 = NULL; - chart->u.lscm.pin2 = NULL; - chart->u.lscm.single_pin = NULL; + chart->u.lscm.pin1 = nullptr; + chart->u.lscm.pin2 = nullptr; + chart->u.lscm.single_pin = nullptr; chart->u.lscm.single_pin_area = 0.0f; } @@ -3264,7 +3264,7 @@ static void p_stretch_pin_boundary(PChart *chart) PVert *v; for (v = chart->verts; v; v = v->nextlink) { - if (v->edge->pair == NULL) { + if (v->edge->pair == nullptr) { v->flag |= PVERT_PIN; } else { @@ -3495,8 +3495,8 @@ static bool p_chart_convex_hull(PChart *chart, PVert ***r_verts, int *r_nverts, *r_nverts = npoints; *r_right = ulen - 1; - MEM_SAFE_FREE(U); - MEM_SAFE_FREE(L); + MEM_freeN(U); + MEM_freeN(L); return true; } @@ -3644,8 +3644,8 @@ static float p_chart_minimum_area_angle(PChart *chart) minangle -= (float)M_PI_2; } - MEM_SAFE_FREE(angles); - MEM_SAFE_FREE(points); + MEM_freeN(angles); + MEM_freeN(points); return minangle; } @@ -3684,9 +3684,9 @@ static void p_chart_rotate_fit_aabb(PChart *chart) /* Exported */ -ParamHandle *GEO_uv_parametrizer_construct_begin(void) +ParamHandle *GEO_uv_parametrizer_construct_begin() { - ParamHandle *handle = (ParamHandle *)MEM_callocN(sizeof(*handle), "ParamHandle"); + ParamHandle *handle = new ParamHandle(); handle->construction_chart = (PChart *)MEM_callocN(sizeof(PChart), "PChart"); handle->state = PHANDLE_STATE_ALLOCATED; handle->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "param construct arena"); @@ -3710,6 +3710,9 @@ void GEO_uv_parametrizer_aspect_ratio(ParamHandle *phandle, float aspx, float as void GEO_uv_parametrizer_delete(ParamHandle *phandle) { + if (!phandle) { + return; + } param_assert(ELEM(phandle->state, PHANDLE_STATE_ALLOCATED, PHANDLE_STATE_CONSTRUCTED)); for (int i = 0; i < phandle->ncharts; i++) { @@ -3719,8 +3722,8 @@ void GEO_uv_parametrizer_delete(ParamHandle *phandle) MEM_SAFE_FREE(phandle->charts); if (phandle->pin_hash) { - BLI_ghash_free(phandle->pin_hash, NULL, NULL); - phandle->pin_hash = NULL; + BLI_ghash_free(phandle->pin_hash, nullptr, nullptr); + phandle->pin_hash = nullptr; } MEM_SAFE_FREE(phandle->construction_chart); @@ -3731,19 +3734,21 @@ void GEO_uv_parametrizer_delete(ParamHandle *phandle) BLI_memarena_free(phandle->arena); BLI_memarena_free(phandle->polyfill_arena); - BLI_heap_free(phandle->polyfill_heap, NULL); + BLI_heap_free(phandle->polyfill_heap, nullptr); - BLI_rng_free(phandle->rng); - phandle->rng = NULL; + if (phandle->rng) { + BLI_rng_free(phandle->rng); + phandle->rng = nullptr; + } - MEM_freeN(phandle); + delete phandle; } -typedef struct GeoUVPinIndex { +using GeoUVPinIndex = struct GeoUVPinIndex { struct GeoUVPinIndex *next; float uv[2]; ParamKey reindex; -} GeoUVPinIndex; +}; /* Find a (mostly) unique ParamKey given a BMVert index and UV co-ordinates. * For each unique pinned UVs, return a unique ParamKey, starting with @@ -3783,7 +3788,7 @@ ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const static GeoUVPinIndex *new_geo_uv_pinindex(ParamHandle *handle, const float uv[2]) { GeoUVPinIndex *pinuv = (GeoUVPinIndex *)BLI_memarena_alloc(handle->arena, sizeof(*pinuv)); - pinuv->next = NULL; + pinuv->next = nullptr; copy_v2_v2(pinuv->uv, uv); pinuv->reindex = PARAM_KEY_MAX - (handle->unique_pin_count++); return pinuv; @@ -3860,7 +3865,7 @@ static void p_add_ngon(ParamHandle *handle, BLI_polyfill_beautify(projverts, nverts, tris, arena, heap); /* Add triangles. */ - for (int j = 0; j < nfilltri; j++) { + for (uint j = 0; j < nfilltri; j++) { uint *tri = tris[j]; uint v0 = tri[0]; uint v1 = tri[1]; @@ -3887,7 +3892,7 @@ void GEO_uv_parametrizer_face_add(ParamHandle *phandle, const bool *pin, const bool *select) { - param_assert(phash_lookup(phandle->hash_faces, key) == NULL); + param_assert(phash_lookup(phandle->hash_faces, key) == nullptr); param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); param_assert(ELEM(nverts, 3, 4)); @@ -3938,12 +3943,13 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle, phandle->ncharts = p_connect_pairs(phandle, topology_from_uvs); phandle->charts = p_split_charts(phandle, chart, phandle->ncharts); - MEM_SAFE_FREE(phandle->construction_chart); + MEM_freeN(phandle->construction_chart); + phandle->construction_chart = nullptr; phash_delete(phandle->hash_verts); phash_delete(phandle->hash_edges); phash_delete(phandle->hash_faces); - phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = NULL; + phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = nullptr; for (i = j = 0; i < phandle->ncharts; i++) { PVert *v; @@ -3952,8 +3958,8 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle, p_chart_boundaries(chart, &outer); if (!topology_from_uvs && chart->nboundaries == 0) { - MEM_SAFE_FREE(chart); - if (count_fail != NULL) { + MEM_freeN(chart); + if (count_fail != nullptr) { *count_fail += 1; } continue; @@ -4018,12 +4024,12 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, in } if (result) { - if (count_changed != NULL) { + if (count_changed != nullptr) { *count_changed += 1; } } else { - if (count_failed != NULL) { + if (count_failed != nullptr) { *count_failed += 1; } } @@ -4214,7 +4220,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, p_chart_uv_translate(chart, trans); p_chart_uv_scale(chart, scale); } - MEM_SAFE_FREE(boxarray); + MEM_freeN(boxarray); if (handle->aspx != handle->aspy) { GEO_uv_parametrizer_scale(handle, handle->aspx, handle->aspy); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 011c79025c4..0fd1d8ff51d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -50,7 +50,7 @@ #include "lineart_intern.h" typedef struct LineartIsecSingle { - float v1[3], v2[3]; + double v1[3], v2[3]; LineartTriangle *tri1, *tri2; } LineartIsecSingle; @@ -1799,7 +1799,7 @@ static void lineart_add_edge_to_array_thread(LineartObjectInfo *obi, LineartEdge * #pe. */ void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count) { - if (pe->max || pe->array) { + if (pe->max || pe->array || count == 0) { return; } @@ -3285,8 +3285,8 @@ static void lineart_add_isec_thread(LineartIsecThread *th, th->array = new_array; } LineartIsecSingle *isec_single = &th->array[th->current]; - copy_v3fl_v3db(isec_single->v1, v1); - copy_v3fl_v3db(isec_single->v2, v2); + copy_v3_v3_db(isec_single->v1, v1); + copy_v3_v3_db(isec_single->v2, v2); isec_single->tri1 = tri1; isec_single->tri2 = tri2; if (tri1->target_reference > tri2->target_reference) { @@ -4582,8 +4582,8 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d) LineartIsecSingle *is = &th->array[j]; LineartVert *v1 = v; LineartVert *v2 = v + 1; - copy_v3db_v3fl(v1->gloc, is->v1); - copy_v3db_v3fl(v2->gloc, is->v2); + copy_v3_v3_db(v1->gloc, is->v1); + copy_v3_v3_db(v2->gloc, is->v2); /* The intersection line has been generated only in geometry space, so we need to transform * them as well. */ mul_v4_m4v3_db(v1->fbcoord, ld->conf.view_projection, v1->gloc); @@ -5227,7 +5227,7 @@ static void lineart_gpencil_generate(LineartCache *cache, } if (shaodow_selection) { if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) { - /* TODO(Yiming): Give a behaviour option for how to display undefined shadow info. */ + /* TODO(Yiming): Give a behavior option for how to display undefined shadow info. */ if ((shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED && (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)))) { continue; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4157caf45d7..65a6a2dc6b7 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -190,8 +190,8 @@ set(OPENGL_SRC set(METAL_SRC metal/mtl_backend.mm - metal/mtl_context.mm metal/mtl_command_buffer.mm + metal/mtl_context.mm metal/mtl_debug.mm metal/mtl_framebuffer.mm metal/mtl_memory.mm @@ -323,6 +323,45 @@ set(GLSL_SRC shaders/common/gpu_shader_common_math_utils.glsl shaders/common/gpu_shader_common_mix_rgb.glsl + shaders/compositor/compositor_alpha_crop.glsl + shaders/compositor/compositor_box_mask.glsl + shaders/compositor/compositor_convert.glsl + shaders/compositor/compositor_ellipse_mask.glsl + shaders/compositor/compositor_flip.glsl + shaders/compositor/compositor_image_crop.glsl + shaders/compositor/compositor_projector_lens_distortion.glsl + shaders/compositor/compositor_realize_on_domain.glsl + shaders/compositor/compositor_screen_lens_distortion.glsl + shaders/compositor/compositor_set_alpha.glsl + shaders/compositor/compositor_split_viewer.glsl + + shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl + shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl + shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl + shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl + shaders/compositor/library/gpu_shader_compositor_color_balance.glsl + shaders/compositor/library/gpu_shader_compositor_color_correction.glsl + shaders/compositor/library/gpu_shader_compositor_color_matte.glsl + shaders/compositor/library/gpu_shader_compositor_color_spill.glsl + shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl + shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl + shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl + shaders/compositor/library/gpu_shader_compositor_exposure.glsl + shaders/compositor/library/gpu_shader_compositor_gamma.glsl + shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl + shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl + shaders/compositor/library/gpu_shader_compositor_invert.glsl + shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl + shaders/compositor/library/gpu_shader_compositor_main.glsl + shaders/compositor/library/gpu_shader_compositor_map_value.glsl + shaders/compositor/library/gpu_shader_compositor_normal.glsl + shaders/compositor/library/gpu_shader_compositor_posterize.glsl + shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl + shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl + shaders/compositor/library/gpu_shader_compositor_store_output.glsl + shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl + shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl + shaders/material/gpu_shader_material_add_shader.glsl shaders/material/gpu_shader_material_ambient_occlusion.glsl shaders/material/gpu_shader_material_anisotropic.glsl @@ -470,8 +509,8 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/overlay/shaders/infos/overlay_grid_info.hh ../draw/engines/overlay/shaders/infos/overlay_outline_info.hh ../draw/engines/overlay/shaders/infos/overlay_paint_info.hh - ../draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh ../draw/engines/overlay/shaders/infos/overlay_sculpt_curves_info.hh + ../draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh ../draw/engines/overlay/shaders/infos/overlay_volume_info.hh ../draw/engines/overlay/shaders/infos/overlay_wireframe_info.hh ../draw/engines/select/shaders/infos/select_id_info.hh @@ -486,6 +525,7 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh ../draw/engines/workbench/shaders/infos/workbench_volume_info.hh ../draw/engines/image/shaders/infos/engine_image_info.hh + ../draw/intern/shaders/draw_debug_info.hh ../draw/intern/shaders/draw_fullscreen_info.hh ../draw/intern/shaders/draw_hair_refine_info.hh ../draw/intern/shaders/draw_object_infos_info.hh @@ -526,6 +566,18 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/gpu_shader_simple_lighting_info.hh shaders/infos/gpu_shader_text_info.hh shaders/infos/gpu_srgb_to_framebuffer_space_info.hh + + shaders/compositor/infos/compositor_alpha_crop_info.hh + shaders/compositor/infos/compositor_box_mask_info.hh + shaders/compositor/infos/compositor_convert_info.hh + shaders/compositor/infos/compositor_ellipse_mask_info.hh + shaders/compositor/infos/compositor_flip_info.hh + shaders/compositor/infos/compositor_image_crop_info.hh + shaders/compositor/infos/compositor_projector_lens_distortion_info.hh + shaders/compositor/infos/compositor_realize_on_domain_info.hh + shaders/compositor/infos/compositor_screen_lens_distortion_info.hh + shaders/compositor/infos/compositor_set_alpha_info.hh + shaders/compositor/infos/compositor_split_viewer_info.hh ) set(SHADER_CREATE_INFOS_CONTENT "") diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 7ef2d81ac31..c085b592a77 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -93,8 +93,10 @@ void GPU_batch_init_ex(GPUBatch *batch, */ void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src); -#define GPU_batch_create(prim, verts, elem) GPU_batch_create_ex(prim, verts, elem, 0) -#define GPU_batch_init(batch, prim, verts, elem) GPU_batch_init_ex(batch, prim, verts, elem, 0) +#define GPU_batch_create(prim, verts, elem) \ + GPU_batch_create_ex(prim, verts, elem, (eGPUBatchFlag)0) +#define GPU_batch_init(batch, prim, verts, elem) \ + GPU_batch_init_ex(batch, prim, verts, elem, (eGPUBatchFlag)0) /** * Same as discard but does not free. (does not call free callback). diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index 6dc49ff494d..d1d91cb7508 100644 --- a/source/blender/gpu/GPU_buffers.h +++ b/source/blender/gpu/GPU_buffers.h @@ -48,7 +48,6 @@ typedef struct GPU_PBVH_Buffers GPU_PBVH_Buffers; * Threaded: do not call any functions that use OpenGL calls! */ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const struct Mesh *mesh, - const struct MVert *vertices, const struct MLoopTri *looptri, const int *sculpt_face_sets, const int *face_indices, diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index c154f1adc8b..529a3da3ab9 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -148,11 +148,19 @@ typedef enum { GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ } GPUUniformBlockBuiltin; +typedef enum { + GPU_STORAGE_BUFFER_DEBUG_VERTS = 0, /* drw_debug_verts_buf */ + GPU_STORAGE_BUFFER_DEBUG_PRINT, /* drw_debug_print_buf */ + + GPU_NUM_STORAGE_BUFFERS, /* Special value, denotes number of builtin buffer blocks. */ +} GPUStorageBufferBuiltin; + void GPU_shader_set_srgb_uniform(GPUShader *shader); int GPU_shader_get_uniform(GPUShader *shader, const char *name); int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin); int GPU_shader_get_builtin_block(GPUShader *shader, int builtin); +int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin); /** DEPRECATED: Kept only because of Python GPU API. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name); int GPU_shader_get_ssbo(GPUShader *shader, const char *name); diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 2a0f624ac45..d64b8b4118a 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -211,11 +211,11 @@ static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers, GPUPrimType prim) * \{ */ static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt, - const MVert *mvert, + const bool *hide_vert, const MLoop *mloop, const int *sculpt_face_sets) { - return (!paint_is_face_hidden(lt, mvert, mloop) && sculpt_face_sets && + return (!paint_is_face_hidden(lt, hide_vert, mloop) && sculpt_face_sets && sculpt_face_sets[lt->poly] > SCULPT_FACE_SET_NONE); } @@ -233,6 +233,9 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, GPUAttrRef vcol_refs[MAX_GPU_ATTR]; GPUAttrRef cd_uvs[MAX_GPU_ATTR]; + const bool *hide_vert = (bool *)CustomData_get_layer_named( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + const CustomDataLayer *actcol = BKE_id_attributes_active_color_get(&mesh->id); eAttrDomain actcol_domain = actcol ? BKE_id_attribute_domain(&mesh->id, actcol) : ATTR_DOMAIN_AUTO; @@ -310,7 +313,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, for (uint i = 0; i < buffers->face_indices_len; i++) { const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]]; - if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { continue; } @@ -350,7 +353,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, buffers->mloop[lt->tri[2]].v, }; - if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { continue; } @@ -390,7 +393,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, buffers->mloop[lt->tri[2]].v, }; - if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { continue; } @@ -454,7 +457,6 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, } GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, - const MVert *vertices, const MLoopTri *looptri, const int *sculpt_face_sets, const int *face_indices, @@ -469,6 +471,9 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers"); + const bool *hide_vert = (bool *)CustomData_get_layer_named( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + /* smooth or flat for all */ buffers->smooth = mpoly[looptri[face_indices[0]].poly].flag & ME_SMOOTH; @@ -477,7 +482,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, /* Count the number of visible triangles */ for (i = 0, tottri = 0; i < face_indices_len; i++) { const MLoopTri *lt = &looptri[face_indices[i]]; - if (gpu_pbvh_is_looptri_visible(lt, vertices, mloop, sculpt_face_sets)) { + if (gpu_pbvh_is_looptri_visible(lt, hide_vert, mloop, sculpt_face_sets)) { int r_edges[3]; BKE_mesh_looptri_get_real_edges(mesh, lt, r_edges); for (int j = 0; j < 3; j++) { @@ -510,7 +515,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, const MLoopTri *lt = &looptri[face_indices[i]]; /* Skip hidden faces */ - if (!gpu_pbvh_is_looptri_visible(lt, vertices, mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, mloop, sculpt_face_sets)) { continue; } diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 8a630d9e3ca..08c768b28ba 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -578,6 +578,12 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin) +{ + ShaderInterface *interface = unwrap(shader)->interface; + return interface->ssbo_builtin((GPUStorageBufferBuiltin)builtin); +} + int GPU_shader_get_ssbo(GPUShader *shader, const char *name) { ShaderInterface *interface = unwrap(shader)->interface; diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index d8af2fc584d..3a14c060484 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -137,7 +137,7 @@ eAttrDomain BKE_id_attribute_domain(const struct ID *UNUSED(id), /** \name Stubs of BKE_paint.h * \{ */ bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt), - const struct MVert *UNUSED(mvert), + const bool *UNUSED(hide_vert), const struct MLoop *UNUSED(mloop)) { BLI_assert_unreachable(); diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index bc0731862cb..110b77f1f52 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -306,6 +306,14 @@ void gpu_shader_create_info_init() info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_); info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_); info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_); + + /* Automatically amend the create info for ease of use of the debug feature. */ + if ((info->builtins_ & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) { + info->additional_info("draw_debug_draw"); + } + if ((info->builtins_ & BuiltinBits::USE_DEBUG_PRINT) == BuiltinBits::USE_DEBUG_PRINT) { + info->additional_info("draw_debug_print"); + } } } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index fb8efbb209a..82defc436e0 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -127,8 +127,12 @@ enum class BuiltinBits { VERTEX_ID = (1 << 14), WORK_GROUP_ID = (1 << 15), WORK_GROUP_SIZE = (1 << 16), + + /* Not a builtin but a flag we use to tag shaders that use the debug features. */ + USE_DEBUG_DRAW = (1 << 29), + USE_DEBUG_PRINT = (1 << 30), }; -ENUM_OPERATORS(BuiltinBits, BuiltinBits::WORK_GROUP_SIZE); +ENUM_OPERATORS(BuiltinBits, BuiltinBits::USE_DEBUG_PRINT); /** * Follow convention described in: diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index aff7df9ac33..961d3fcfe5b 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -11,6 +11,7 @@ #include <algorithm> #include <iomanip> #include <iostream> +#include <regex> #include <sstream> #include "BLI_ghash.h" @@ -42,7 +43,7 @@ struct GPUSource { StringRefNull source; Vector<GPUSource *> dependencies; bool dependencies_init = false; - shader::BuiltinBits builtins = (shader::BuiltinBits)0; + shader::BuiltinBits builtins = shader::BuiltinBits::NONE; std::string processed_source; GPUSource(const char *path, @@ -54,46 +55,45 @@ struct GPUSource { /* Scan for builtins. */ /* FIXME: This can trigger false positive caused by disabled #if blocks. */ /* TODO(fclem): Could be made faster by scanning once. */ - if (source.find("gl_FragCoord", 0)) { + if (source.find("gl_FragCoord", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::FRAG_COORD; } - if (source.find("gl_FrontFacing", 0)) { + if (source.find("gl_FrontFacing", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::FRONT_FACING; } - if (source.find("gl_GlobalInvocationID", 0)) { + if (source.find("gl_GlobalInvocationID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::GLOBAL_INVOCATION_ID; } - if (source.find("gl_InstanceID", 0)) { + if (source.find("gl_InstanceID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::INSTANCE_ID; } - if (source.find("gl_LocalInvocationID", 0)) { + if (source.find("gl_LocalInvocationID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::LOCAL_INVOCATION_ID; } - if (source.find("gl_LocalInvocationIndex", 0)) { + if (source.find("gl_LocalInvocationIndex", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::LOCAL_INVOCATION_INDEX; } - if (source.find("gl_NumWorkGroup", 0)) { + if (source.find("gl_NumWorkGroup", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::NUM_WORK_GROUP; } - if (source.find("gl_PointCoord", 0)) { + if (source.find("gl_PointCoord", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::POINT_COORD; } - if (source.find("gl_PointSize", 0)) { + if (source.find("gl_PointSize", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::POINT_SIZE; } - if (source.find("gl_PrimitiveID", 0)) { + if (source.find("gl_PrimitiveID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::PRIMITIVE_ID; } - if (source.find("gl_VertexID", 0)) { + if (source.find("gl_VertexID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::VERTEX_ID; } - if (source.find("gl_WorkGroupID", 0)) { + if (source.find("gl_WorkGroupID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::WORK_GROUP_ID; } - if (source.find("gl_WorkGroupSize", 0)) { + if (source.find("gl_WorkGroupSize", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::WORK_GROUP_SIZE; } - /* TODO(fclem): We could do that at compile time. */ /* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */ if (filename.endswith(".h") || filename.endswith(".hh")) { @@ -101,6 +101,18 @@ struct GPUSource { quote_preprocess(); } else { + if (source.find("'") != StringRef::not_found) { + char_literals_preprocess(); + } + if (source.find("drw_print") != StringRef::not_found) { + string_preprocess(); + } + if ((source.find("drw_debug_") != StringRef::not_found) && + /* Avoid theses two files where it makes no sense to add the dependency. */ + (filename != "common_debug_draw_lib.glsl" && + filename != "draw_debug_draw_display_vert.glsl")) { + builtins |= shader::BuiltinBits::USE_DEBUG_DRAW; + } check_no_quotes(); } @@ -522,6 +534,217 @@ struct GPUSource { } } + void char_literals_preprocess() + { + const StringRefNull input = source; + std::stringstream output; + int64_t cursor = -1; + int64_t last_pos = 0; + + while (true) { + cursor = find_token(input, '\'', cursor + 1); + if (cursor == -1) { + break; + } + /* Output anything between 2 print statement. */ + output << input.substr(last_pos, cursor - last_pos); + + /* Extract string. */ + int64_t char_start = cursor + 1; + int64_t char_end = find_token(input, '\'', char_start); + CHECK(char_end, input, cursor, "Malformed char literal. Missing ending `'`."); + + StringRef input_char = input.substr(char_start, char_end - char_start); + if (input_char.size() == 0) { + CHECK(-1, input, cursor, "Malformed char literal. Empty character constant"); + } + + uint8_t char_value = input_char[0]; + + if (input_char[0] == '\\') { + if (input_char[1] == 'n') { + char_value = '\n'; + } + else { + CHECK(-1, input, cursor, "Unsupported escaped character"); + } + } + else { + if (input_char.size() > 1) { + CHECK(-1, input, cursor, "Malformed char literal. Multi-character character constant"); + } + } + + char hex[8]; + SNPRINTF(hex, "0x%.2Xu", char_value); + output << hex; + + cursor = last_pos = char_end + 1; + } + /* If nothing has been changed, do not allocate processed_source. */ + if (last_pos == 0) { + return; + } + + if (last_pos != 0) { + output << input.substr(last_pos); + } + processed_source = output.str(); + source = processed_source.c_str(); + } + + /* Replace print(string) by equivalent drw_print_char4() sequence. */ + void string_preprocess() + { + const StringRefNull input = source; + std::stringstream output; + int64_t cursor = -1; + int64_t last_pos = 0; + + while (true) { + cursor = find_keyword(input, "drw_print", cursor + 1); + if (cursor == -1) { + break; + } + + bool do_endl = false; + StringRef func = input.substr(cursor); + if (func.startswith("drw_print(")) { + do_endl = true; + } + else if (func.startswith("drw_print_no_endl(")) { + do_endl = false; + } + else { + continue; + } + + /* Output anything between 2 print statement. */ + output << input.substr(last_pos, cursor - last_pos); + + /* Extract string. */ + int64_t str_start = input.find('(', cursor) + 1; + int64_t semicolon = find_token(input, ';', str_start + 1); + CHECK(semicolon, input, cursor, "Malformed print(). Missing `;` ."); + int64_t str_end = rfind_token(input, ')', semicolon); + if (str_end < str_start) { + CHECK(-1, input, cursor, "Malformed print(). Missing closing `)` ."); + } + + std::stringstream sub_output; + StringRef input_args = input.substr(str_start, str_end - str_start); + + auto print_string = [&](std::string str) -> int { + size_t len_before_pad = str.length(); + /* Pad string to uint size. */ + while (str.length() % 4 != 0) { + str += " "; + } + /* Keep everything in one line to not mess with the shader logs. */ + sub_output << "/* " << str << "*/"; + sub_output << "drw_print_string_start(" << len_before_pad << ");"; + for (size_t i = 0; i < len_before_pad; i += 4) { + uint8_t chars[4] = {*(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0), + *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1), + *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2), + *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3)}; + if (i + 4 > len_before_pad) { + chars[len_before_pad - i] = '\0'; + } + char uint_hex[12]; + SNPRINTF(uint_hex, "0x%.2X%.2X%.2X%.2Xu", chars[3], chars[2], chars[1], chars[0]); + sub_output << "drw_print_char4(" << StringRefNull(uint_hex) << ");"; + } + return 0; + }; + + std::string func_args = input_args; + /* Workaround to support function call inside prints. We replace commas by a non control + * character `$` in order to use simpler regex later. */ + bool string_scope = false; + int func_scope = 0; + for (char &c : func_args) { + if (c == '"') { + string_scope = !string_scope; + } + else if (!string_scope) { + if (c == '(') { + func_scope++; + } + else if (c == ')') { + func_scope--; + } + else if (c == ',' && func_scope != 0) { + c = '$'; + } + } + } + + const bool print_as_variable = (input_args[0] != '"') && find_token(input_args, ',') == -1; + if (print_as_variable) { + /* Variable or expression debugging. */ + std::string arg = input_args; + /* Pad align most values. */ + while (arg.length() % 4 != 0) { + arg += " "; + } + print_string(arg); + print_string("= "); + sub_output << "drw_print_value(" << input_args << ");"; + } + else { + const std::regex arg_regex( + /* String args. */ + "[\\s]*\"([^\r\n\t\f\v\"]*)\"" + /* OR. */ + "|" + /* value args. */ + "([^,]+)"); + std::smatch args_match; + std::string::const_iterator args_search_start(func_args.cbegin()); + while (std::regex_search(args_search_start, func_args.cend(), args_match, arg_regex)) { + args_search_start = args_match.suffix().first; + std::string arg_string = args_match[1].str(); + std::string arg_val = args_match[2].str(); + + if (arg_string.empty()) { + for (char &c : arg_val) { + if (c == '$') { + c = ','; + } + } + sub_output << "drw_print_value(" << arg_val << ");"; + } + else { + print_string(arg_string); + } + } + } + + if (do_endl) { + sub_output << "drw_print_newline();"; + } + + output << sub_output.str(); + + cursor = last_pos = str_end + 1; + } + /* If nothing has been changed, do not allocate processed_source. */ + if (last_pos == 0) { + return; + } + + if (filename != "common_debug_print_lib.glsl") { + builtins |= shader::BuiltinBits::USE_DEBUG_PRINT; + } + + if (last_pos != 0) { + output << input.substr(last_pos); + } + processed_source = output.str(); + source = processed_source.c_str(); + } + #undef find_keyword #undef rfind_keyword #undef find_token @@ -537,6 +760,15 @@ struct GPUSource { this->dependencies_init = true; int64_t pos = -1; + using namespace shader; + /* Auto dependency injection for debug capabilities. */ + if ((builtins & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) { + dependencies.append_non_duplicates(dict.lookup("common_debug_draw_lib.glsl")); + } + if ((builtins & BuiltinBits::USE_DEBUG_PRINT) == BuiltinBits::USE_DEBUG_PRINT) { + dependencies.append_non_duplicates(dict.lookup("common_debug_print_lib.glsl")); + } + while (true) { GPUSource *dependency_source = nullptr; @@ -558,6 +790,7 @@ struct GPUSource { return 1; } } + /* Recursive. */ int result = dependency_source->init_dependencies(dict, g_functions); if (result != 0) { @@ -583,7 +816,7 @@ struct GPUSource { shader::BuiltinBits builtins_get() const { - shader::BuiltinBits out_builtins = shader::BuiltinBits::NONE; + shader::BuiltinBits out_builtins = builtins; for (auto *dep : dependencies) { out_builtins |= dep->builtins; } diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index 60344757b43..812244c9b3a 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -56,6 +56,7 @@ class ShaderInterface { /** Location of builtin uniforms. Fast access, no lookup needed. */ int32_t builtins_[GPU_NUM_UNIFORMS]; int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]; + int32_t builtin_buffers_[GPU_NUM_STORAGE_BUFFERS]; public: ShaderInterface(); @@ -116,9 +117,17 @@ class ShaderInterface { return builtin_blocks_[builtin]; } + /* Returns binding position. */ + inline int32_t ssbo_builtin(const GPUStorageBufferBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_STORAGE_BUFFERS); + return builtin_buffers_[builtin]; + } + protected: static inline const char *builtin_uniform_name(GPUUniformBuiltin u); static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u); + static inline const char *builtin_storage_block_name(GPUStorageBufferBuiltin u); inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const; inline void copy_input_name(ShaderInput *input, @@ -212,6 +221,18 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu } } +inline const char *ShaderInterface::builtin_storage_block_name(GPUStorageBufferBuiltin u) +{ + switch (u) { + case GPU_STORAGE_BUFFER_DEBUG_VERTS: + return "drw_debug_verts_buf"; + case GPU_STORAGE_BUFFER_DEBUG_PRINT: + return "drw_debug_print_buf"; + default: + return nullptr; + } +} + /* Returns string length including '\0' terminator. */ inline uint32_t ShaderInterface::set_input_name(ShaderInput *input, char *name, diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm index dfda0a8de7f..f574140531d 100644 --- a/source/blender/gpu/metal/mtl_query.mm +++ b/source/blender/gpu/metal/mtl_query.mm @@ -9,7 +9,7 @@ namespace blender::gpu { static const size_t VISIBILITY_COUNT_PER_BUFFER = 512; -/* defined in the documentation but not queryable programmatically: +/* Defined in the documentation but can't be queried programmatically: * https://developer.apple.com/documentation/metal/mtlvisibilityresultmode/mtlvisibilityresultmodeboolean?language=objc */ static const size_t VISIBILITY_RESULT_SIZE_IN_BYTES = 8; diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 1b3ab2941a8..4623a14dab3 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -318,6 +318,13 @@ GLShaderInterface::GLShaderInterface(GLuint program) builtin_blocks_[u] = (block != nullptr) ? block->binding : -1; } + /* Builtin Storage Buffers */ + for (int32_t u_int = 0; u_int < GPU_NUM_STORAGE_BUFFERS; u_int++) { + GPUStorageBufferBuiltin u = static_cast<GPUStorageBufferBuiltin>(u_int); + const ShaderInput *block = this->ssbo_get(builtin_storage_block_name(u)); + builtin_buffers_[u] = (block != nullptr) ? block->binding : -1; + } + MEM_freeN(uniforms_from_blocks); /* Resize name buffer to save some memory. */ @@ -481,6 +488,13 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI builtin_blocks_[u] = (block != nullptr) ? block->binding : -1; } + /* Builtin Storage Buffers */ + for (int32_t u_int = 0; u_int < GPU_NUM_STORAGE_BUFFERS; u_int++) { + GPUStorageBufferBuiltin u = static_cast<GPUStorageBufferBuiltin>(u_int); + const ShaderInput *block = this->ssbo_get(builtin_storage_block_name(u)); + builtin_buffers_[u] = (block != nullptr) ? block->binding : -1; + } + this->sort_inputs(); // this->debug_print(); diff --git a/source/blender/gpu/opengl/gl_storage_buffer.cc b/source/blender/gpu/opengl/gl_storage_buffer.cc index 4592adc3a61..83a56edcf04 100644 --- a/source/blender/gpu/opengl/gl_storage_buffer.cc +++ b/source/blender/gpu/opengl/gl_storage_buffer.cc @@ -72,7 +72,7 @@ void GLStorageBuf::bind(int slot) if (slot >= GLContext::max_ssbo_binds) { fprintf( stderr, - "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.", + "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.\n", name_, slot, GLContext::max_ssbo_binds); diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc index e58cea9de43..022fbcfdf29 100644 --- a/source/blender/gpu/opengl/gl_uniform_buffer.cc +++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc @@ -65,11 +65,12 @@ void GLUniformBuf::update(const void *data) void GLUniformBuf::bind(int slot) { if (slot >= GLContext::max_ubo_binds) { - fprintf(stderr, - "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.", - name_, - slot, - GLContext::max_ubo_binds); + fprintf( + stderr, + "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.\n", + name_, + slot, + GLContext::max_ubo_binds); return; } diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl index fe89985ae7f..33108d3a989 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl @@ -140,6 +140,84 @@ void hsl_to_rgb(vec4 hsl, out vec4 outcol) outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w); } +/* ** YCCA to RGBA ** */ + +void ycca_to_rgba_itu_601(vec4 ycca, out vec4 color) +{ + ycca.xyz *= 255.0; + ycca.xyz -= vec3(16.0, 128.0, 128.0); + color.rgb = mat3(vec3(1.164), 0.0, -0.392, 2.017, 1.596, -0.813, 0.0) * ycca.xyz; + color.rgb /= 255.0; + color.a = ycca.a; +} + +void ycca_to_rgba_itu_709(vec4 ycca, out vec4 color) +{ + ycca.xyz *= 255.0; + ycca.xyz -= vec3(16.0, 128.0, 128.0); + color.rgb = mat3(vec3(1.164), 0.0, -0.213, 2.115, 1.793, -0.534, 0.0) * ycca.xyz; + color.rgb /= 255.0; + color.a = ycca.a; +} + +void ycca_to_rgba_jpeg(vec4 ycca, out vec4 color) +{ + ycca.xyz *= 255.0; + color.rgb = mat3(vec3(1.0), 0.0, -0.34414, 1.772, 1.402, -0.71414, 0.0) * ycca.xyz; + color.rgb += vec3(-179.456, 135.45984, -226.816); + color.rgb /= 255.0; + color.a = ycca.a; +} + +/* ** RGBA to YCCA ** */ + +void rgba_to_ycca_itu_601(vec4 rgba, out vec4 ycca) +{ + rgba.rgb *= 255.0; + ycca.xyz = mat3(0.257, -0.148, 0.439, 0.504, -0.291, -0.368, 0.098, 0.439, -0.071) * rgba.rgb; + ycca.xyz += vec3(16.0, 128.0, 128.0); + ycca.xyz /= 255.0; + ycca.a = rgba.a; +} + +void rgba_to_ycca_itu_709(vec4 rgba, out vec4 ycca) +{ + rgba.rgb *= 255.0; + ycca.xyz = mat3(0.183, -0.101, 0.439, 0.614, -0.338, -0.399, 0.062, 0.439, -0.040) * rgba.rgb; + ycca.xyz += vec3(16.0, 128.0, 128.0); + ycca.xyz /= 255.0; + ycca.a = rgba.a; +} + +void rgba_to_ycca_jpeg(vec4 rgba, out vec4 ycca) +{ + rgba.rgb *= 255.0; + ycca.xyz = mat3(0.299, -0.16874, 0.5, 0.587, -0.33126, -0.41869, 0.114, 0.5, -0.08131) * + rgba.rgb; + ycca.xyz += vec3(0.0, 128.0, 128.0); + ycca.xyz /= 255.0; + ycca.a = rgba.a; +} + +/* ** YUVA to RGBA ** */ + +void yuva_to_rgba_itu_709(vec4 yuva, out vec4 color) +{ + color.rgb = mat3(vec3(1.0), 0.0, -0.21482, 2.12798, 1.28033, -0.38059, 0.0) * yuva.xyz; + color.a = yuva.a; +} + +/* ** RGBA to YUVA ** */ + +void rgba_to_yuva_itu_709(vec4 rgba, out vec4 yuva) +{ + yuva.xyz = mat3(0.2126, -0.09991, 0.615, 0.7152, -0.33609, -0.55861, 0.0722, 0.436, -0.05639) * + rgba.rgb; + yuva.a = rgba.a; +} + +/* ** Alpha Handling ** */ + void color_alpha_clear(vec4 color, out vec4 result) { result = vec4(color.rgb, 1.0); @@ -147,15 +225,50 @@ void color_alpha_clear(vec4 color, out vec4 result) void color_alpha_premultiply(vec4 color, out vec4 result) { - result = vec4(color.rgb * color.a, 1.0); + result = vec4(color.rgb * color.a, color.a); } void color_alpha_unpremultiply(vec4 color, out vec4 result) { if (color.a == 0.0 || color.a == 1.0) { - result = vec4(color.rgb, 1.0); + result = color; } else { - result = vec4(color.rgb / color.a, 1.0); + result = vec4(color.rgb / color.a, color.a); + } +} + +float linear_rgb_to_srgb(float color) +{ + if (color < 0.0031308) { + return (color < 0.0) ? 0.0 : color * 12.92; + } + + return 1.055 * pow(color, 1.0 / 2.4) - 0.055; +} + +vec3 linear_rgb_to_srgb(vec3 color) +{ + return vec3( + linear_rgb_to_srgb(color.r), linear_rgb_to_srgb(color.g), linear_rgb_to_srgb(color.b)); +} + +float srgb_to_linear_rgb(float color) +{ + if (color < 0.04045) { + return (color < 0.0) ? 0.0 : color * (1.0 / 12.92); } + + return pow((color + 0.055) * (1.0 / 1.055), 2.4); +} + +vec3 srgb_to_linear_rgb(vec3 color) +{ + return vec3( + srgb_to_linear_rgb(color.r), srgb_to_linear_rgb(color.g), srgb_to_linear_rgb(color.b)); +} + +float get_luminance(vec3 color, vec3 luminance_coefficients) +{ + return dot(color, luminance_coefficients); } diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl index 8948ed77557..db8e114ec7a 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl @@ -95,6 +95,81 @@ void curves_combined_only(float factor, result = mix(color, result, factor); } +/* Contrary to standard tone curve implementations, the film-like implementation tries to preserve + * the hue of the colors as much as possible. To understand why this might be a problem, consider + * the violet color (0.5, 0.0, 1.0). If this color was to be evaluated at a power curve x^4, the + * color will be blue (0.0625, 0.0, 1.0). So the color changes and not just its luminosity, which + * is what film-like tone curves tries to avoid. + * + * First, the channels with the lowest and highest values are identified and evaluated at the + * curve. Then, the third channel---the median---is computed while maintaining the original hue of + * the color. To do that, we look at the equation for deriving the hue from RGB values. Assuming + * the maximum, minimum, and median channels are known, and ignoring the 1/3 period offset of the + * hue, the equation is: + * + * hue = (median - min) / (max - min) [1] + * + * Since we have the new values for the minimum and maximum after evaluating at the curve, we also + * have: + * + * hue = (new_median - new_min) / (new_max - new_min) [2] + * + * Since we want the hue to be equivalent, by equating [1] and [2] and rearranging: + * + * (new_median - new_min) / (new_max - new_min) = (median - min) / (max - min) + * new_median - new_min = (new_max - new_min) * (median - min) / (max - min) + * new_median = new_min + (new_max - new_min) * (median - min) / (max - min) + * new_median = new_min + (median - min) * ((new_max - new_min) / (max - min)) [QED] + * + * Which gives us the median color that preserves the hue. More intuitively, the median is computed + * such that the change in the distance from the median to the minimum is proportional to the + * change in the distance from the minimum to the maximum. Finally, each of the new minimum, + * maximum, and median values are written to the color channel that they were originally extracted + * from. */ +void curves_film_like(float factor, + vec4 color, + vec4 black_level, + vec4 white_level, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out vec4 result) +{ + vec4 balanced = white_balance(color, black_level, white_level); + + /* Find the maximum, minimum, and median of the color channels. */ + float minimum = min(balanced.r, min(balanced.g, balanced.b)); + float maximum = max(balanced.r, max(balanced.g, balanced.b)); + float median = max(min(balanced.r, balanced.g), min(balanced.b, max(balanced.r, balanced.g))); + + /* Evaluate alpha curve map at the maximum and minimum channels. The alpha curve is the Combined + * curve in the UI. */ + float min_parameter = NORMALIZE_PARAMETER(minimum, range_minimum, range_divider); + float max_parameter = NORMALIZE_PARAMETER(maximum, range_minimum, range_divider); + float new_min = texture(curve_map, vec2(min_parameter, layer)).a; + float new_max = texture(curve_map, vec2(max_parameter, layer)).a; + + /* Then, extrapolate if needed. */ + new_min = extrapolate_if_needed(min_parameter, new_min, start_slope, end_slope); + new_max = extrapolate_if_needed(max_parameter, new_max, start_slope, end_slope); + + /* Compute the new median using the ratio between the new and the original range. */ + float scaling_ratio = (new_max - new_min) / (maximum - minimum); + float new_median = new_min + (median - minimum) * scaling_ratio; + + /* Write each value to its original channel. */ + bvec3 channel_is_min = equal(balanced.rgb, vec3(minimum)); + vec3 median_or_min = mix(vec3(new_median), vec3(new_min), channel_is_min); + bvec3 channel_is_max = equal(balanced.rgb, vec3(maximum)); + result.rgb = mix(median_or_min, vec3(new_max), channel_is_max); + result.a = color.a; + + result = mix(color, result, clamp(factor, 0.0, 1.0)); +} + void curves_vector(vec3 vector, sampler1DArray curve_map, const float layer, diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl index 124654963fd..1ba22b4c5da 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl @@ -34,6 +34,17 @@ float compatible_pow(float x, float y) return pow(x, y); } +/* A version of pow that returns a fallback value if the computation is undefined. From the spec: + * The result is undefined if x < 0 or if x = 0 and y is less than or equal 0. */ +float fallback_pow(float x, float y, float fallback) +{ + if (x < 0.0 || (x == 0.0 && y <= 0.0)) { + return fallback; + } + + return pow(x, y); +} + float wrap(float a, float b, float c) { float range = b - c; @@ -114,8 +125,24 @@ void vector_copy(vec3 normal, out vec3 outnormal) outnormal = normal; } +vec3 fallback_pow(vec3 a, float b, vec3 fallback) +{ + return vec3(fallback_pow(a.x, b, fallback.x), + fallback_pow(a.y, b, fallback.y), + fallback_pow(a.z, b, fallback.z)); +} + /* Matirx Math */ +/* Return a 2D rotation matrix with the angle that the input 2D vector makes with the x axis. */ +mat2 vector_to_rotation_matrix(vec2 vector) +{ + vec2 normalized_vector = normalize(vector); + float cos_angle = normalized_vector.x; + float sin_angle = normalized_vector.y; + return mat2(cos_angle, sin_angle, -sin_angle, cos_angle); +} + mat3 euler_to_mat3(vec3 euler) { float cx = cos(euler.x); diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl index f9652f1150b..39f3c722dd2 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl @@ -2,28 +2,24 @@ void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, col2, fac); outcol.a = col1.a; } void mix_add(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, col1 + col2, fac); outcol.a = col1.a; } void mix_mult(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, col1 * col2, fac); outcol.a = col1.a; } void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1); @@ -32,7 +28,6 @@ void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = col1; @@ -61,14 +56,30 @@ void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_sub(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, col1 - col2, fac); outcol.a = col1.a; } void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); + float facm = 1.0 - fac; + + outcol = vec4(vec3(0.0), col1.a); + + if (col2.r != 0.0) { + outcol.r = facm * col1.r + fac * col1.r / col2.r; + } + if (col2.g != 0.0) { + outcol.g = facm * col1.g + fac * col1.g / col2.g; + } + if (col2.b != 0.0) { + outcol.b = facm * col1.b + fac * col1.b / col2.b; + } +} + +/* A variant of mix_div that fallback to the first color upon zero division. */ +void mix_div_fallback(float fac, vec4 col1, vec4 col2, out vec4 outcol) +{ float facm = 1.0 - fac; outcol = col1; @@ -86,28 +97,24 @@ void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, abs(col1 - col2), fac); outcol.a = col1.a; } void mix_dark(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac); outcol.a = col1.a; } void mix_light(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac); outcol.a = col1.a; } void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = col1; if (outcol.r != 0.0) { @@ -150,7 +157,6 @@ void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float tmp, facm = 1.0 - fac; outcol = col1; @@ -200,7 +206,6 @@ void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = col1; @@ -220,7 +225,6 @@ void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = col1; @@ -238,7 +242,6 @@ void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; vec4 hsv, hsv2; @@ -251,7 +254,6 @@ void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = col1; @@ -272,22 +274,26 @@ void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_soft(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; vec4 one = vec4(1.0); vec4 scr = one - (one - col2) * (one - col1); outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr); + outcol.a = col1.a; } void mix_linear(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); - outcol = col1 + fac * (2.0 * (col2 - vec4(0.5))); + outcol.a = col1.a; } -void clamp_color(vec3 vec, vec3 min, vec3 max, out vec3 out_vec) +void clamp_color(vec4 vec, const vec4 min, const vec4 max, out vec4 out_vec) { out_vec = clamp(vec, min, max); } + +void multiply_by_alpha(float factor, vec4 color, out float result) +{ + result = factor * color.a; +} diff --git a/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl b/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl new file mode 100644 index 00000000000..d55c8efd4c6 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl @@ -0,0 +1,11 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + /* The lower bound is inclusive and upper bound is exclusive. */ + bool is_inside = all(greaterThanEqual(texel, lower_bound)) && all(lessThan(texel, upper_bound)); + /* Write the pixel color if it is inside the cropping region, otherwise, write zero. */ + vec4 color = is_inside ? texture_load(input_tx, texel) : vec4(0.0); + imageStore(output_img, texel, color); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl new file mode 100644 index 00000000000..fad23f28fde --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl @@ -0,0 +1,27 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1)); + uv -= location; + uv.y *= float(domain_size.y) / float(domain_size.x); + uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv; + bool is_inside = all(lessThan(abs(uv), size)); + + float base_mask_value = texture_load(base_mask_tx, texel).x; + float value = texture_load(mask_value_tx, texel).x; + +#if defined(CMP_NODE_MASKTYPE_ADD) + float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value; +#elif defined(CMP_NODE_MASKTYPE_SUBTRACT) + float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value; +#elif defined(CMP_NODE_MASKTYPE_MULTIPLY) + float output_mask_value = is_inside ? base_mask_value * value : 0.0; +#elif defined(CMP_NODE_MASKTYPE_NOT) + float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value; +#endif + + imageStore(output_mask_img, texel, vec4(output_mask_value)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_convert.glsl b/source/blender/gpu/shaders/compositor/compositor_convert.glsl new file mode 100644 index 00000000000..044fb057ca5 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_convert.glsl @@ -0,0 +1,8 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 value = texture_load(input_tx, texel); + imageStore(output_img, texel, CONVERT_EXPRESSION(value)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl new file mode 100644 index 00000000000..28f725067e0 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl @@ -0,0 +1,27 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1)); + uv -= location; + uv.y *= float(domain_size.y) / float(domain_size.x); + uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv; + bool is_inside = length(uv / radius) < 1.0; + + float base_mask_value = texture_load(base_mask_tx, texel).x; + float value = texture_load(mask_value_tx, texel).x; + +#if defined(CMP_NODE_MASKTYPE_ADD) + float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value; +#elif defined(CMP_NODE_MASKTYPE_SUBTRACT) + float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value; +#elif defined(CMP_NODE_MASKTYPE_MULTIPLY) + float output_mask_value = is_inside ? base_mask_value * value : 0.0; +#elif defined(CMP_NODE_MASKTYPE_NOT) + float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value; +#endif + + imageStore(output_mask_img, texel, vec4(output_mask_value)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_flip.glsl b/source/blender/gpu/shaders/compositor/compositor_flip.glsl new file mode 100644 index 00000000000..919c454ee63 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_flip.glsl @@ -0,0 +1,15 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = texture_size(input_tx); + ivec2 flipped_texel = texel; + if (flip_x) { + flipped_texel.x = size.x - texel.x - 1; + } + if (flip_y) { + flipped_texel.y = size.y - texel.y - 1; + } + imageStore(output_img, texel, texture_load(input_tx, flipped_texel)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl b/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl new file mode 100644 index 00000000000..f20e033dee4 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl @@ -0,0 +1,7 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + imageStore(output_img, texel, texture_load(input_tx, texel + lower_bound)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl b/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl new file mode 100644 index 00000000000..cf961b20b34 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* Get the normalized coordinates of the pixel centers. */ + vec2 normalized_texel = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx)); + + /* Sample the red and blue channels shifted by the dispersion amount. */ + const float red = texture(input_tx, normalized_texel + vec2(dispersion, 0.0)).r; + const float green = texture_load(input_tx, texel).g; + const float blue = texture(input_tx, normalized_texel - vec2(dispersion, 0.0)).b; + + imageStore(output_img, texel, vec4(red, green, blue, 1.0)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl b/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl new file mode 100644 index 00000000000..b2961d07219 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl @@ -0,0 +1,25 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* First, transform the input image by transforming the domain coordinates with the inverse of + * input image's transformation. The inverse transformation is an affine matrix and thus the + * coordinates should be in homogeneous coordinates. */ + vec2 coordinates = (mat3(inverse_transformation) * vec3(texel, 1.0)).xy; + + /* Since an input image with an identity transformation is supposed to be centered in the domain, + * we subtract the offset between the lower left corners of the input image and the domain, which + * is half the difference between their sizes, because the difference in size is on both sides of + * the centered image. */ + ivec2 domain_size = imageSize(domain_img); + ivec2 input_size = texture_size(input_tx); + vec2 offset = (domain_size - input_size) / 2.0; + + /* Subtract the offset and divide by the input image size to get the relevant coordinates into + * the sampler's expected [0, 1] range. */ + vec2 normalized_coordinates = (coordinates - offset) / input_size; + + imageStore(domain_img, texel, texture(input_tx, normalized_coordinates)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl b/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl new file mode 100644 index 00000000000..dc572ea5aaf --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl @@ -0,0 +1,151 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +/* A model that approximates lens distortion parameterized by a distortion parameter and dependent + * on the squared distance to the center of the image. The distorted pixel is then computed as the + * scalar multiplication of the pixel coordinates with the value returned by this model. See the + * compute_distorted_uv function for more details. */ +float compute_distortion_scale(float distortion, float distance_squared) +{ + return 1.0 / (1.0 + sqrt(max(0.0, 1.0 - distortion * distance_squared))); +} + +/* A vectorized version of compute_distortion_scale that is applied on the chromatic distortion + * parameters passed to the shader. */ +vec3 compute_chromatic_distortion_scale(float distance_squared) +{ + return 1.0 / (1.0 + sqrt(max(vec3(0.0), 1.0 - chromatic_distortion * distance_squared))); +} + +/* Compute the image coordinates after distortion by the given distortion scale computed by the + * compute_distortion_scale function. Note that the function expects centered normalized UV + * coordinates but outputs non-centered image coordinates. */ +vec2 compute_distorted_uv(vec2 uv, float scale) +{ + return (uv * scale + 0.5) * texture_size(input_tx) - 0.5; +} + +/* Compute the number of integration steps that should be used to approximate the distorted pixel + * using a heuristic, see the compute_number_of_steps function for more details. The numbers of + * steps is proportional to the number of pixels spanned by the distortion amount. For jitter + * distortion, the square root of the distortion amount plus 1 is used with a minimum of 2 steps. + * For non-jitter distortion, the distortion amount plus 1 is used as the number of steps */ +int compute_number_of_integration_steps_heuristic(float distortion) +{ +#if defined(JITTER) + return distortion < 4.0 ? 2 : int(sqrt(distortion + 1.0)); +#else + return int(distortion + 1.0); +#endif +} + +/* Compute the number of integration steps that should be used to compute each channel of the + * distorted pixel. Each of the channels are distorted by their respective chromatic distortion + * amount, then the amount of distortion between each two consecutive channels is computed, this + * amount is then used to heuristically infer the number of needed integration steps, see the + * integrate_distortion function for more information. */ +ivec3 compute_number_of_integration_steps(vec2 uv, float distance_squared) +{ + /* Distort each channel by its respective chromatic distortion amount. */ + vec3 distortion_scale = compute_chromatic_distortion_scale(distance_squared); + vec2 distorted_uv_red = compute_distorted_uv(uv, distortion_scale.r); + vec2 distorted_uv_green = compute_distorted_uv(uv, distortion_scale.g); + vec2 distorted_uv_blue = compute_distorted_uv(uv, distortion_scale.b); + + /* Infer the number of needed integration steps to compute the distorted red channel starting + * from the green channel. */ + float distortion_red = distance(distorted_uv_red, distorted_uv_green); + int steps_red = compute_number_of_integration_steps_heuristic(distortion_red); + + /* Infer the number of needed integration steps to compute the distorted blue channel starting + * from the green channel. */ + float distortion_blue = distance(distorted_uv_green, distorted_uv_blue); + int steps_blue = compute_number_of_integration_steps_heuristic(distortion_blue); + + /* The number of integration steps used to compute the green channel is the sum of both the red + * and the blue channel steps because it is computed once with each of them. */ + return ivec3(steps_red, steps_red + steps_blue, steps_blue); +} + +/* Returns a random jitter amount, which is essentially a random value in the [0, 1] range. If + * jitter is not enabled, return a constant 0.5 value instead. */ +float get_jitter(int seed) +{ +#if defined(JITTER) + return hash_uint3_to_float(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y, seed); +#else + return 0.5; +#endif +} + +/* Each color channel may have a different distortion with the guarantee that the red will have the + * lowest distortion while the blue will have the highest one. If each channel is distorted + * independently, the image will look disintegrated, with each channel seemingly merely shifted. + * Consequently, the distorted pixels needs to be computed by integrating along the path of change + * of distortion starting from one channel to another. For instance, to compute the distorted red + * from the distorted green, we accumulate the color of the distorted pixel starting from the + * distortion of the red, taking small steps until we reach the distortion of the green. The pixel + * color is weighted such that it is maximum at the start distortion and zero at the end distortion + * in an arithmetic progression. The integration steps can be augmented with random values to + * simulate lens jitter. Finally, it should be noted that this function integrates both the start + * and end channels in reverse directions for more efficient computation. */ +vec3 integrate_distortion(int start, int end, float distance_squared, vec2 uv, int steps) +{ + vec3 accumulated_color = vec3(0.0); + float distortion_amount = chromatic_distortion[end] - chromatic_distortion[start]; + for (int i = 0; i < steps; i++) { + /* The increment will be in the [0, 1) range across iterations. */ + float increment = (i + get_jitter(i)) / steps; + float distortion = chromatic_distortion[start] + increment * distortion_amount; + float distortion_scale = compute_distortion_scale(distortion, distance_squared); + + /* Sample the color at the distorted coordinates and accumulate it weighted by the increment + * value for both the start and end channels. */ + vec2 distorted_uv = compute_distorted_uv(uv, distortion_scale); + vec4 color = texture(input_tx, distorted_uv / texture_size(input_tx)); + accumulated_color[start] += (1.0 - increment) * color[start]; + accumulated_color[end] += increment * color[end]; + } + return accumulated_color; +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* Compute the UV image coordinates in the range [-1, 1] as well as the squared distance to the + * center of the image, which is at (0, 0) in the UV coordinates. */ + vec2 center = texture_size(input_tx) / 2.0; + vec2 uv = scale * (texel + 0.5 - center) / center; + float distance_squared = dot(uv, uv); + + /* If any of the color channels will get distorted outside of the screen beyond what is possible, + * write a zero transparent color and return. */ + if (any(greaterThan(chromatic_distortion * distance_squared, vec3(1.0)))) { + imageStore(output_img, texel, vec4(0.0)); + return; + } + + /* Compute the number of integration steps that should be used to compute each channel of the + * distorted pixel. */ + ivec3 number_of_steps = compute_number_of_integration_steps(uv, distance_squared); + + /* Integrate the distortion of the red and green, then the green and blue channels. That means + * the green will be integrated twice, but this is accounted for in the number of steps which the + * color will later be divided by. See the compute_number_of_integration_steps function for more + * details. */ + vec3 color = vec3(0.0); + color += integrate_distortion(0, 1, distance_squared, uv, number_of_steps.r); + color += integrate_distortion(1, 2, distance_squared, uv, number_of_steps.b); + + /* The integration above performed weighted accumulation, and thus the color needs to be divided + * by the sum of the weights. Assuming no jitter, the weights are generated as an arithmetic + * progression starting from (0.5 / n) to ((n - 0.5) / n) for n terms. The sum of an arithmetic + * progression can be computed as (n * (start + end) / 2), which when subsisting the start and + * end reduces to (n / 2). So the color should be multiplied by 2 / n. The jitter sequence + * approximately sums to the same value because it is a uniform random value whose mean value is + * 0.5, so the expression doesn't change regardless of jitter. */ + color *= 2.0 / vec3(number_of_steps); + + imageStore(output_img, texel, vec4(color, 1.0)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl new file mode 100644 index 00000000000..7dd40581790 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl @@ -0,0 +1,8 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 color = vec4(texture_load(image_tx, texel).rgb, texture_load(alpha_tx, texel).x); + imageStore(output_img, texel, color); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl new file mode 100644 index 00000000000..866b9045da2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl @@ -0,0 +1,14 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); +#if defined(SPLIT_HORIZONTAL) + bool condition = (view_size.x * split_ratio) < texel.x; +#elif defined(SPLIT_VERTICAL) + bool condition = (view_size.y * split_ratio) < texel.y; +#endif + vec4 color = condition ? texture_load(first_image_tx, texel) : + texture_load(second_image_tx, texel); + imageStore(output_img, texel, color); +} diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh new file mode 100644 index 00000000000..11f2f329cd8 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_alpha_crop) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "lower_bound") + .push_constant(Type::IVEC2, "upper_bound") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_alpha_crop.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh new file mode 100644 index 00000000000..ecb253bbab1 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_box_mask_shared) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "domain_size") + .push_constant(Type::VEC2, "location") + .push_constant(Type::VEC2, "size") + .push_constant(Type::FLOAT, "cos_angle") + .push_constant(Type::FLOAT, "sin_angle") + .sampler(0, ImageType::FLOAT_2D, "base_mask_tx") + .sampler(1, ImageType::FLOAT_2D, "mask_value_tx") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img") + .compute_source("compositor_box_mask.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_box_mask_add) + .additional_info("compositor_box_mask_shared") + .define("CMP_NODE_MASKTYPE_ADD") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_box_mask_subtract) + .additional_info("compositor_box_mask_shared") + .define("CMP_NODE_MASKTYPE_SUBTRACT") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_box_mask_multiply) + .additional_info("compositor_box_mask_shared") + .define("CMP_NODE_MASKTYPE_MULTIPLY") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_box_mask_not) + .additional_info("compositor_box_mask_shared") + .define("CMP_NODE_MASKTYPE_NOT") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh new file mode 100644 index 00000000000..35e60056736 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_convert_shared) + .local_group_size(16, 16) + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .typedef_source("gpu_shader_compositor_type_conversion.glsl") + .compute_source("compositor_convert.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_convert_float_to_vector) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_float(value.x), 0.0)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_float_to_color) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4_from_float(value.x)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_float) + .additional_info("compositor_convert_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(float_from_vec4(value), vec3(0.0))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_vector) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_vec4(value), 0.0)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_vector_to_float) + .additional_info("compositor_convert_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(float_from_vec3(value.xyz), vec3(0.0))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_vector_to_color) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4_from_vec3(value.xyz)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_extract_alpha_from_color) + .additional_info("compositor_convert_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(value.a, vec3(0.0))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_half_color) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "value") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_float_to_half_float) + .additional_info("compositor_convert_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(value.r, vec3(0.0))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_opaque) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(value.rgb, 1.0)") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh new file mode 100644 index 00000000000..52db91c94e5 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_shared) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "domain_size") + .push_constant(Type::VEC2, "location") + .push_constant(Type::VEC2, "radius") + .push_constant(Type::FLOAT, "cos_angle") + .push_constant(Type::FLOAT, "sin_angle") + .sampler(0, ImageType::FLOAT_2D, "base_mask_tx") + .sampler(1, ImageType::FLOAT_2D, "mask_value_tx") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img") + .compute_source("compositor_ellipse_mask.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_add) + .additional_info("compositor_ellipse_mask_shared") + .define("CMP_NODE_MASKTYPE_ADD") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_subtract) + .additional_info("compositor_ellipse_mask_shared") + .define("CMP_NODE_MASKTYPE_SUBTRACT") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_multiply) + .additional_info("compositor_ellipse_mask_shared") + .define("CMP_NODE_MASKTYPE_MULTIPLY") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_not) + .additional_info("compositor_ellipse_mask_shared") + .define("CMP_NODE_MASKTYPE_NOT") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh new file mode 100644 index 00000000000..db831518cb7 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_flip) + .local_group_size(16, 16) + .push_constant(Type::BOOL, "flip_x") + .push_constant(Type::BOOL, "flip_y") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_flip.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh new file mode 100644 index 00000000000..e7736744c40 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_image_crop) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "lower_bound") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_image_crop.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh new file mode 100644 index 00000000000..98fe1731703 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_projector_lens_distortion) + .local_group_size(16, 16) + .push_constant(Type::FLOAT, "dispersion") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_projector_lens_distortion.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh new file mode 100644 index 00000000000..4528649ae98 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_shared) + .local_group_size(16, 16) + .push_constant(Type::MAT4, "inverse_transformation") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .compute_source("compositor_realize_on_domain.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_color) + .additional_info("compositor_realize_on_domain_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_vector) + .additional_info("compositor_realize_on_domain_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_float) + .additional_info("compositor_realize_on_domain_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh new file mode 100644 index 00000000000..c42f2b328d4 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion_shared) + .local_group_size(16, 16) + .push_constant(Type::VEC3, "chromatic_distortion") + .push_constant(Type::FLOAT, "scale") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_screen_lens_distortion.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion) + .additional_info("compositor_screen_lens_distortion_shared") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion_jitter) + .additional_info("compositor_screen_lens_distortion_shared") + .define("JITTER") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh new file mode 100644 index 00000000000..ca28194e921 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_set_alpha) + .local_group_size(16, 16) + .sampler(0, ImageType::FLOAT_2D, "image_tx") + .sampler(1, ImageType::FLOAT_2D, "alpha_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_set_alpha.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh new file mode 100644 index 00000000000..d5793b0ce59 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_shared) + .local_group_size(16, 16) + .push_constant(Type::FLOAT, "split_ratio") + .push_constant(Type::IVEC2, "view_size") + .sampler(0, ImageType::FLOAT_2D, "first_image_tx") + .sampler(1, ImageType::FLOAT_2D, "second_image_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_split_viewer.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_horizontal) + .additional_info("compositor_split_viewer_shared") + .define("SPLIT_HORIZONTAL") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_vertical) + .additional_info("compositor_split_viewer_shared") + .define("SPLIT_VERTICAL") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl new file mode 100644 index 00000000000..8e3e033147f --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl @@ -0,0 +1,48 @@ +void node_composite_alpha_over_mixed( + float factor, vec4 color, vec4 over_color, float premultiply_factor, out vec4 result) +{ + if (over_color.a <= 0.0) { + result = color; + } + else if (factor == 1.0 && over_color.a >= 1.0) { + result = over_color; + } + else { + float add_factor = 1.0 - premultiply_factor + over_color.a * premultiply_factor; + float premultiplier = factor * add_factor; + float multiplier = 1.0 - factor * over_color.a; + + result = multiplier * color + vec2(premultiplier, factor).xxxy * over_color; + } +} + +void node_composite_alpha_over_key(float factor, vec4 color, vec4 over_color, out vec4 result) +{ + if (over_color.a <= 0.0) { + result = color; + } + else if (factor == 1.0 && over_color.a >= 1.0) { + result = over_color; + } + else { + result = mix(color, vec4(over_color.rgb, 1.0), factor * over_color.a); + } +} + +void node_composite_alpha_over_premultiply(float factor, + vec4 color, + vec4 over_color, + out vec4 result) +{ + if (over_color.a < 0.0) { + result = color; + } + else if (factor == 1.0 && over_color.a >= 1.0) { + result = over_color; + } + else { + float multiplier = 1.0 - factor * over_color.a; + + result = multiplier * color + factor * over_color; + } +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl new file mode 100644 index 00000000000..ce71b4fd8a4 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl @@ -0,0 +1,38 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +/* The algorithm is by Werner D. Streidt + * (http://visca.com/ffactory/archives/5-99/msg00021.html) + * Extracted of OpenCV demhist.c + */ + +#define FLT_EPSILON 1.192092896e-07F + +void node_composite_bright_contrast( + vec4 color, float brightness, float contrast, const float use_premultiply, out vec4 result) +{ + brightness /= 100.0; + float delta = contrast / 200.0; + + float multiplier, offset; + if (contrast > 0.0) { + multiplier = 1.0 - delta * 2.0; + multiplier = 1.0 / max(multiplier, FLT_EPSILON); + offset = multiplier * (brightness - delta); + } + else { + delta *= -1.0; + multiplier = max(1.0 - delta * 2.0, 0.0); + offset = multiplier * brightness + delta; + } + + if (use_premultiply != 0.0) { + color_alpha_unpremultiply(color, color); + } + + result.rgb = color.rgb * multiplier + offset; + result.a = color.a; + + if (use_premultiply != 0.0) { + color_alpha_premultiply(result, result); + } +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl new file mode 100644 index 00000000000..f2dcc9543f2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl @@ -0,0 +1,52 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +#define CMP_NODE_CHANNEL_MATTE_CS_RGB 1.0 +#define CMP_NODE_CHANNEL_MATTE_CS_HSV 2.0 +#define CMP_NODE_CHANNEL_MATTE_CS_YUV 3.0 +#define CMP_NODE_CHANNEL_MATTE_CS_YCC 4.0 + +void node_composite_channel_matte(vec4 color, + const float color_space, + const float matte_channel, + const vec2 limit_channels, + float max_limit, + float min_limit, + out vec4 result, + out float matte) +{ + vec4 channels; + if (color_space == CMP_NODE_CHANNEL_MATTE_CS_HSV) { + rgb_to_hsv(color, channels); + } + else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YUV) { + rgba_to_yuva_itu_709(color, channels); + } + else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YCC) { + rgba_to_ycca_itu_709(color, channels); + } + else { + channels = color; + } + + float matte_value = channels[int(matte_channel)]; + float limit_value = max(channels[int(limit_channels.x)], channels[int(limit_channels.y)]); + + float alpha = 1.0 - (matte_value - limit_value); + if (alpha > max_limit) { + alpha = color.a; + } + else if (alpha < min_limit) { + alpha = 0.0; + } + else { + alpha = (alpha - min_limit) / (max_limit - min_limit); + } + + matte = min(alpha, color.a); + result = color * matte; +} + +#undef CMP_NODE_CHANNEL_MATTE_CS_RGB +#undef CMP_NODE_CHANNEL_MATTE_CS_HSV +#undef CMP_NODE_CHANNEL_MATTE_CS_YUV +#undef CMP_NODE_CHANNEL_MATTE_CS_YCC diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl new file mode 100644 index 00000000000..5d6bea0c9db --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl @@ -0,0 +1,43 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +/* Algorithm from the book Video Demystified. Chapter 7. Chroma Keying. */ +void node_composite_chroma_matte(vec4 color, + vec4 key, + float acceptance, + float cutoff, + float falloff, + out vec4 result, + out float matte) +{ + vec4 color_ycca; + rgba_to_ycca_itu_709(color, color_ycca); + vec4 key_ycca; + rgba_to_ycca_itu_709(key, key_ycca); + + /* Normalize the CrCb components into the [-1, 1] range. */ + vec2 color_cc = color_ycca.yz * 2.0 - 1.0; + vec2 key_cc = key_ycca.yz * 2.0 - 1.0; + + /* Rotate the color onto the space of the key such that x axis of the color space passes through + * the key color. */ + color_cc = vector_to_rotation_matrix(key_cc * vec2(1.0, -1.0)) * color_cc; + + /* Compute foreground key. If positive, the value is in the [0, 1] range. */ + float foreground_key = color_cc.x - (abs(color_cc.y) / acceptance); + + /* Negative foreground key values retain the original alpha. Positive values are scaled by the + * falloff, while colors that make an angle less than the cutoff angle get a zero alpha. */ + float alpha = color.a; + if (foreground_key > 0.0) { + alpha = 1.0 - (foreground_key / falloff); + + if (abs(atan(color_cc.y, color_cc.x)) < (cutoff / 2.0)) { + alpha = 0.0; + } + } + + /* Compute output. */ + matte = min(alpha, color.a); + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl new file mode 100644 index 00000000000..bffb94cdedb --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl @@ -0,0 +1,34 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_color_balance_lgg( + float factor, vec4 color, vec3 lift, vec3 gamma, vec3 gain, out vec4 result) +{ + lift = 2.0 - lift; + vec3 srgb_color = linear_rgb_to_srgb(color.rgb); + vec3 lift_balanced = ((srgb_color - 1.0) * lift) + 1.0; + + vec3 gain_balanced = lift_balanced * gain; + gain_balanced = max(gain_balanced, vec3(0.0)); + + vec3 linear_color = srgb_to_linear_rgb(gain_balanced); + gamma = mix(gamma, vec3(1e-6), equal(gamma, vec3(0.0))); + vec3 gamma_balanced = pow(linear_color, 1.0 / gamma); + + result.rgb = mix(color.rgb, gamma_balanced, min(factor, 1.0)); + result.a = color.a; +} + +void node_composite_color_balance_asc_cdl(float factor, + vec4 color, + vec3 offset, + vec3 power, + vec3 slope, + float offset_basis, + out vec4 result) +{ + offset += offset_basis; + vec3 balanced = color.rgb * slope + offset; + balanced = pow(max(balanced, vec3(0.0)), power); + result.rgb = mix(color.rgb, balanced, min(factor, 1.0)); + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl new file mode 100644 index 00000000000..9b4858f03be --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl @@ -0,0 +1,87 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_color_correction(vec4 color, + float mask, + const vec3 enabled_channels, + float start_midtones, + float end_midtones, + float master_saturation, + float master_contrast, + float master_gamma, + float master_gain, + float master_lift, + float shadows_saturation, + float shadows_contrast, + float shadows_gamma, + float shadows_gain, + float shadows_lift, + float midtones_saturation, + float midtones_contrast, + float midtones_gamma, + float midtones_gain, + float midtones_lift, + float highlights_saturation, + float highlights_contrast, + float highlights_gamma, + float highlights_gain, + float highlights_lift, + const vec3 luminance_coefficients, + out vec4 result) +{ + const float margin = 0.10; + const float margin_divider = 0.5 / margin; + float level = (color.r + color.g + color.b) / 3.0; + float level_shadows = 0.0; + float level_midtones = 0.0; + float level_highlights = 0.0; + if (level < (start_midtones - margin)) { + level_shadows = 1.0; + } + else if (level < (start_midtones + margin)) { + level_midtones = ((level - start_midtones) * margin_divider) + 0.5; + level_shadows = 1.0 - level_midtones; + } + else if (level < (end_midtones - margin)) { + level_midtones = 1.0; + } + else if (level < (end_midtones + margin)) { + level_highlights = ((level - end_midtones) * margin_divider) + 0.5; + level_midtones = 1.0 - level_highlights; + } + else { + level_highlights = 1.0; + } + + float contrast = level_shadows * shadows_contrast; + contrast += level_midtones * midtones_contrast; + contrast += level_highlights * highlights_contrast; + contrast *= master_contrast; + float saturation = level_shadows * shadows_saturation; + saturation += level_midtones * midtones_saturation; + saturation += level_highlights * highlights_saturation; + saturation *= master_saturation; + float gamma = level_shadows * shadows_gamma; + gamma += level_midtones * midtones_gamma; + gamma += level_highlights * highlights_gamma; + gamma *= master_gamma; + float gain = level_shadows * shadows_gain; + gain += level_midtones * midtones_gain; + gain += level_highlights * highlights_gain; + gain *= master_gain; + float lift = level_shadows * shadows_lift; + lift += level_midtones * midtones_lift; + lift += level_highlights * highlights_lift; + lift += master_lift; + + float inverse_gamma = 1.0 / gamma; + float luma = get_luminance(color.rgb, luminance_coefficients); + + vec3 corrected = luma + saturation * (color.rgb - luma); + corrected = 0.5 + (corrected - 0.5) * contrast; + corrected = fallback_pow(corrected * gain + lift, inverse_gamma, corrected); + corrected = mix(color.rgb, corrected, min(mask, 1.0)); + + result.rgb = mix(corrected, color.rgb, equal(enabled_channels, vec3(0.0))); + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl new file mode 100644 index 00000000000..038471bc1bc --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl @@ -0,0 +1,27 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_color_matte(vec4 color, + vec4 key, + float hue_epsilon, + float saturation_epsilon, + float value_epsilon, + out vec4 result, + out float matte) + +{ + vec4 color_hsva; + rgb_to_hsv(color, color_hsva); + vec4 key_hsva; + rgb_to_hsv(key, key_hsva); + + bool is_within_saturation = distance(color_hsva.y, key_hsva.y) < saturation_epsilon; + bool is_within_value = distance(color_hsva.z, key_hsva.z) < value_epsilon; + bool is_within_hue = distance(color_hsva.x, key_hsva.x) < hue_epsilon; + /* Hue wraps around, so check the distance around the boundary. */ + float min_hue = min(color_hsva.x, key_hsva.x); + float max_hue = max(color_hsva.x, key_hsva.x); + is_within_hue = is_within_hue || ((min_hue + (1.0 - max_hue)) < hue_epsilon); + + matte = (is_within_hue && is_within_saturation && is_within_value) ? 0.0 : color.a; + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl new file mode 100644 index 00000000000..0adad53ad80 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl @@ -0,0 +1,13 @@ +void node_composite_color_spill(vec4 color, + float factor, + const float spill_channel, + vec3 spill_scale, + const vec2 limit_channels, + float limit_scale, + out vec4 result) +{ + float average_limit = (color[int(limit_channels.x)] + color[int(limit_channels.y)]) / 2.0; + float map = factor * color[int(spill_channel)] - limit_scale * average_limit; + result.rgb = map > 0.0 ? color.rgb + spill_scale * map : color.rgb; + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl new file mode 100644 index 00000000000..bcdd625bd4f --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl @@ -0,0 +1,6 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void color_to_luminance(vec4 color, const vec3 luminance_coefficients, out float result) +{ + result = get_luminance(color.rgb, luminance_coefficients); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl new file mode 100644 index 00000000000..d769cadce3c --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl @@ -0,0 +1,10 @@ +void node_composite_difference_matte( + vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte) +{ + vec4 difference = abs(color - key); + float average_difference = (difference.r + difference.g + difference.b) / 3.0; + bool is_opaque = average_difference > tolerance + falloff; + float alpha = is_opaque ? color.a : (max(0.0, average_difference - tolerance) / falloff); + matte = min(alpha, color.a); + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl new file mode 100644 index 00000000000..9beed66826c --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl @@ -0,0 +1,26 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_distance_matte_rgba( + vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte) +{ + float difference = distance(color.rgb, key.rgb); + bool is_opaque = difference > tolerance + falloff; + float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff; + matte = min(alpha, color.a); + result = color * matte; +} + +void node_composite_distance_matte_ycca( + vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte) +{ + vec4 color_ycca; + rgba_to_ycca_itu_709(color, color_ycca); + vec4 key_ycca; + rgba_to_ycca_itu_709(key, key_ycca); + + float difference = distance(color_ycca.yz, key_ycca.yz); + bool is_opaque = difference > tolerance + falloff; + float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff; + matte = min(alpha, color.a); + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl new file mode 100644 index 00000000000..f246635a91e --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl @@ -0,0 +1,6 @@ +void node_composite_exposure(vec4 color, float exposure, out vec4 result) +{ + float multiplier = exp2(exposure); + result.rgb = color.rgb * multiplier; + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl new file mode 100644 index 00000000000..53070d4b0e2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl @@ -0,0 +1,7 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) + +void node_composite_gamma(vec4 color, float gamma, out vec4 result) +{ + result.rgb = fallback_pow(color.rgb, gamma, color.rgb); + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl new file mode 100644 index 00000000000..99eb125cdf2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl @@ -0,0 +1,39 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize + * parameters accordingly. */ +#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range) + +void node_composite_hue_correct(float factor, + vec4 color, + sampler1DArray curve_map, + const float layer, + vec3 minimums, + vec3 range_dividers, + out vec4 result) +{ + vec4 hsv; + rgb_to_hsv(color, hsv); + + /* First, adjust the hue channel on its own, since corrections in the saturation and value + * channels depends on the new value of the hue, not its original value. A curve map value of 0.5 + * means no change in hue, so adjust the value to get an identity at 0.5. Since the identity of + * addition is 0, we subtract 0.5 (0.5 - 0.5 = 0). */ + const float hue_parameter = NORMALIZE_PARAMETER(hsv.x, minimums.x, range_dividers.x); + hsv.x += texture(curve_map, vec2(hue_parameter, layer)).x - 0.5; + + /* Second, adjust the saturation and value based on the new value of the hue. A curve map value + * of 0.5 means no change in hue, so adjust the value to get an identity at 0.5. Since the + * identity of duplication is 1, we multiply by 2 (0.5 * 2 = 1). */ + vec2 parameters = NORMALIZE_PARAMETER(hsv.x, minimums.yz, range_dividers.yz); + hsv.y *= texture(curve_map, vec2(parameters.x, layer)).y * 2.0; + hsv.z *= texture(curve_map, vec2(parameters.y, layer)).z * 2.0; + + /* Sanitize the new hue and saturation values. */ + hsv.x = fract(hsv.x); + hsv.y = clamp(hsv.y, 0.0, 1.0); + + hsv_to_rgb(hsv, result); + + result = mix(color, result, factor); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl new file mode 100644 index 00000000000..dd5eb33d318 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_hue_saturation_value( + vec4 color, float hue, float saturation, float value, float factor, out vec4 result) +{ + vec4 hsv; + rgb_to_hsv(color, hsv); + + hsv.x = fract(hsv.x + hue + 0.5); + hsv.y = clamp(hsv.y * saturation, 0.0, 1.0); + hsv.z = hsv.z * value; + + hsv_to_rgb(hsv, result); + + result = mix(color, result, factor); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl new file mode 100644 index 00000000000..59be746da7f --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl @@ -0,0 +1,13 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_invert(float fac, vec4 color, float do_rgb, float do_alpha, out vec4 result) +{ + result = color; + if (do_rgb != 0.0) { + result.rgb = 1.0 - result.rgb; + } + if (do_alpha != 0.0) { + result.a = 1.0 - result.a; + } + result = mix(color, result, fac); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl new file mode 100644 index 00000000000..3647ac583fe --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl @@ -0,0 +1,14 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_luminance_matte(vec4 color, + float high, + float low, + const vec3 luminance_coefficients, + out vec4 result, + out float matte) +{ + float luminance = get_luminance(color.rgb, luminance_coefficients); + float alpha = clamp(0.0, 1.0, (luminance - low) / (high - low)); + matte = min(alpha, color.a); + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl new file mode 100644 index 00000000000..27624223dbc --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl @@ -0,0 +1,7 @@ +/* The compute shader that will be dispatched by the compositor ShaderOperation. It just calls the + * evaluate function that will be dynamically generated and appended to this shader in the + * ShaderOperation::generate_code method. */ +void main() +{ + evaluate(); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl new file mode 100644 index 00000000000..20874b4ef44 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl @@ -0,0 +1,56 @@ +/* An arbitrary value determined by Blender. */ +#define BLENDER_ZMAX 10000.0 + +void node_composite_map_range(float value, + float from_min, + float from_max, + float to_min, + float to_max, + const float should_clamp, + out float result) +{ + if (abs(from_max - from_min) < 1e-6) { + result = 0.0; + } + else { + if (value >= -BLENDER_ZMAX && value <= BLENDER_ZMAX) { + result = (value - from_min) / (from_max - from_min); + result = to_min + result * (to_max - to_min); + } + else if (value > BLENDER_ZMAX) { + result = to_max; + } + else { + result = to_min; + } + + if (should_clamp != 0.0) { + if (to_max > to_min) { + result = clamp(result, to_min, to_max); + } + else { + result = clamp(result, to_max, to_min); + } + } + } +} + +void node_composite_map_value(float value, + float offset, + float size, + const float use_min, + float min, + const float use_max, + float max, + out float result) +{ + result = (value + offset) * size; + + if (use_min != 0.0 && result < min) { + result = min; + } + + if (use_max != 0.0 && result > max) { + result = max; + } +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl new file mode 100644 index 00000000000..a2e3b6c4aaa --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl @@ -0,0 +1,9 @@ +void node_composite_normal(vec3 input_vector, + vec3 input_normal, + out vec3 result_normal, + out float result_dot) +{ + vec3 normal = normalize(input_normal); + result_normal = normal; + result_dot = -dot(input_vector, normal); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl new file mode 100644 index 00000000000..ee8ae234abe --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl @@ -0,0 +1,6 @@ +void node_composite_posterize(vec4 color, float steps, out vec4 result) +{ + steps = clamp(steps, 2.0, 1024.0); + result = floor(color * steps) / steps; + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl new file mode 100644 index 00000000000..d72d2260394 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl @@ -0,0 +1,132 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +/* ** Combine/Separate XYZ ** */ + +void node_composite_combine_xyz(float x, float y, float z, out vec3 vector) +{ + vector = vec3(x, y, z); +} + +void node_composite_separate_xyz(vec3 vector, out float x, out float y, out float z) +{ + x = vector.x; + y = vector.y; + z = vector.z; +} + +/* ** Combine/Separate RGBA ** */ + +void node_composite_combine_rgba(float r, float g, float b, float a, out vec4 color) +{ + color = vec4(r, g, b, a); +} + +void node_composite_separate_rgba(vec4 color, out float r, out float g, out float b, out float a) +{ + r = color.r; + g = color.g; + b = color.b; + a = color.a; +} + +/* ** Combine/Separate HSVA ** */ + +void node_composite_combine_hsva(float h, float s, float v, float a, out vec4 color) +{ + hsv_to_rgb(vec4(h, s, v, a), color); +} + +void node_composite_separate_hsva(vec4 color, out float h, out float s, out float v, out float a) +{ + vec4 hsva; + rgb_to_hsv(color, hsva); + h = hsva.x; + s = hsva.y; + v = hsva.z; + a = hsva.a; +} + +/* ** Combine/Separate HSLA ** */ + +void node_composite_combine_hsla(float h, float s, float l, float a, out vec4 color) +{ + hsl_to_rgb(vec4(h, s, l, a), color); +} + +void node_composite_separate_hsla(vec4 color, out float h, out float s, out float l, out float a) +{ + vec4 hsla; + rgb_to_hsl(color, hsla); + h = hsla.x; + s = hsla.y; + l = hsla.z; + a = hsla.a; +} + +/* ** Combine/Separate YCCA ** */ + +void node_composite_combine_ycca_itu_601(float y, float cb, float cr, float a, out vec4 color) +{ + ycca_to_rgba_itu_601(vec4(y, cb, cr, a), color); +} + +void node_composite_combine_ycca_itu_709(float y, float cb, float cr, float a, out vec4 color) +{ + ycca_to_rgba_itu_709(vec4(y, cb, cr, a), color); +} + +void node_composite_combine_ycca_jpeg(float y, float cb, float cr, float a, out vec4 color) +{ + ycca_to_rgba_jpeg(vec4(y, cb, cr, a), color); +} + +void node_composite_separate_ycca_itu_601( + vec4 color, out float y, out float cb, out float cr, out float a) +{ + vec4 ycca; + rgba_to_ycca_itu_601(color, ycca); + y = ycca.x; + cb = ycca.y; + cr = ycca.z; + a = ycca.a; +} + +void node_composite_separate_ycca_itu_709( + vec4 color, out float y, out float cb, out float cr, out float a) +{ + vec4 ycca; + rgba_to_ycca_itu_709(color, ycca); + y = ycca.x; + cb = ycca.y; + cr = ycca.z; + a = ycca.a; +} + +void node_composite_separate_ycca_jpeg( + vec4 color, out float y, out float cb, out float cr, out float a) +{ + vec4 ycca; + rgba_to_ycca_jpeg(color, ycca); + y = ycca.x; + cb = ycca.y; + cr = ycca.z; + a = ycca.a; +} + +/* ** Combine/Separate YUVA ** */ + +void node_composite_combine_yuva_itu_709(float y, float u, float v, float a, out vec4 color) +{ + yuva_to_rgba_itu_709(vec4(y, u, v, a), color); +} + +void node_composite_separate_yuva_itu_709( + vec4 color, out float y, out float u, out float v, out float a) +{ + vec4 yuva; + rgba_to_yuva_itu_709(color, yuva); + y = yuva.x; + u = yuva.y; + v = yuva.z; + a = yuva.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl new file mode 100644 index 00000000000..95380d1ed0f --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl @@ -0,0 +1,9 @@ +void node_composite_set_alpha_apply(vec4 color, float alpha, out vec4 result) +{ + result = color * alpha; +} + +void node_composite_set_alpha_replace(vec4 color, float alpha, out vec4 result) +{ + result = vec4(color.rgb, alpha); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl new file mode 100644 index 00000000000..7fba26907b5 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl @@ -0,0 +1,26 @@ +/* The following functions are called to store the given value in the output identified by the + * given ID. The ID is an unsigned integer that is encoded in a float, so floatBitsToUint is called + * to get the actual identifier. The functions have an output value as their last argument that is + * used to establish an output link that is then used to track the nodes that contribute to the + * output of the compositor node tree. + * + * The store_[float|vector|color] functions are dynamically generated in + * ShaderOperation::generate_code_for_outputs. */ + +void node_compositor_store_output_float(const float id, float value, out float out_value) +{ + store_float(floatBitsToUint(id), value); + out_value = value; +} + +void node_compositor_store_output_vector(const float id, vec3 vector, out vec3 out_vector) +{ + store_vector(floatBitsToUint(id), vector); + out_vector = vector; +} + +void node_compositor_store_output_color(const float id, vec4 color, out vec4 out_color) +{ + store_color(floatBitsToUint(id), color); + out_color = color; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl new file mode 100644 index 00000000000..00e9a391097 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl @@ -0,0 +1,25 @@ +/* A shorthand for 1D textureSize with a zero LOD. */ +int texture_size(sampler1D sampler) +{ + return textureSize(sampler, 0); +} + +/* A shorthand for 1D texelFetch with zero LOD and bounded access clamped to border. */ +vec4 texture_load(sampler1D sampler, int x) +{ + const int texture_bound = texture_size(sampler) - 1; + return texelFetch(sampler, clamp(x, 0, texture_bound), 0); +} + +/* A shorthand for 2D textureSize with a zero LOD. */ +ivec2 texture_size(sampler2D sampler) +{ + return textureSize(sampler, 0); +} + +/* A shorthand for 2D texelFetch with zero LOD and bounded access clamped to border. */ +vec4 texture_load(sampler2D sampler, ivec2 texel) +{ + const ivec2 texture_bounds = texture_size(sampler) - ivec2(1); + return texelFetch(sampler, clamp(texel, ivec2(0), texture_bounds), 0); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl new file mode 100644 index 00000000000..75c76fd7341 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl @@ -0,0 +1,29 @@ +float float_from_vec4(vec4 vector) +{ + return dot(vector.rgb, vec3(1.0)) / 3.0; +} + +float float_from_vec3(vec3 vector) +{ + return dot(vector, vec3(1.0)) / 3.0; +} + +vec3 vec3_from_vec4(vec4 vector) +{ + return vector.rgb; +} + +vec3 vec3_from_float(float value) +{ + return vec3(value); +} + +vec4 vec4_from_vec3(vec3 vector) +{ + return vec4(vector, 1.0); +} + +vec4 vec4_from_float(float value) +{ + return vec4(vec3(value), 1.0); +} diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h index 0818dd653a1..2fb1d814c83 100644 --- a/source/blender/imbuf/IMB_colormanagement.h +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -56,6 +56,8 @@ bool IMB_colormanagement_space_name_is_data(const char *name); bool IMB_colormanagement_space_name_is_scene_linear(const char *name); bool IMB_colormanagement_space_name_is_srgb(const char *name); +BLI_INLINE void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3]); + /** * Convert a float RGB triplet to the correct luminance weighted average. * diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 1b32bef0a98..5ad226a26f2 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -251,11 +251,11 @@ typedef struct ImBuf { int refcounter; /* some parameters to pass along for packing images */ - /** Compressed image only used with png and exr currently */ + /** Compressed image only used with PNG and EXR currently */ unsigned char *encodedbuffer; - /** Size of data written to encodedbuffer */ + /** Size of data written to `encodedbuffer`. */ unsigned int encodedsize; - /** Size of encodedbuffer */ + /** Size of `encodedbuffer` */ unsigned int encodedbuffersize; /* color management */ diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c index 668307ec802..3c6c0f5fd0a 100644 --- a/source/blender/imbuf/intern/colormanagement_inline.c +++ b/source/blender/imbuf/intern/colormanagement_inline.c @@ -11,6 +11,11 @@ #include "BLI_math_vector.h" #include "IMB_colormanagement_intern.h" +void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3]) +{ + copy_v3_v3(r_rgb, imbuf_luma_coefficients); +} + float IMB_colormanagement_get_luminance(const float rgb[3]) { return dot_v3v3(imbuf_luma_coefficients, rgb); diff --git a/source/blender/io/collada/EffectExporter.cpp b/source/blender/io/collada/EffectExporter.cpp index 71a54e3a7c9..40ce20617fc 100644 --- a/source/blender/io/collada/EffectExporter.cpp +++ b/source/blender/io/collada/EffectExporter.cpp @@ -46,6 +46,7 @@ EffectsExporter::EffectsExporter(COLLADASW::StreamWriter *sw, bool EffectsExporter::hasEffects(Scene *sce) { + bool result = false; FOREACH_SCENE_OBJECT_BEGIN (sce, ob) { int a; for (a = 0; a < ob->totcol; a++) { @@ -56,11 +57,12 @@ bool EffectsExporter::hasEffects(Scene *sce) continue; } - return true; + result = true; + break; } } FOREACH_SCENE_OBJECT_END; - return false; + return result; } void EffectsExporter::exportEffects(bContext *C, Scene *sce) diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index a6818c0bf5d..ee5c6a0a47f 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -19,15 +19,15 @@ set(SRC intern/dupli_parent_finder.cc intern/dupli_persistent_id.cc intern/object_identifier.cc - intern/path_util.cc intern/orientation.c + intern/path_util.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh + IO_orientation.h IO_path_util.hh IO_path_util_types.h IO_types.h - IO_orientation.h intern/dupli_parent_finder.hh ) diff --git a/source/blender/io/stl/CMakeLists.txt b/source/blender/io/stl/CMakeLists.txt index e0c48bbbf7e..3a21da5c579 100644 --- a/source/blender/io/stl/CMakeLists.txt +++ b/source/blender/io/stl/CMakeLists.txt @@ -24,16 +24,16 @@ set(INC_SYS set(SRC IO_stl.cc - importer/stl_import_mesh.cc + importer/stl_import.cc importer/stl_import_ascii_reader.cc importer/stl_import_binary_reader.cc - importer/stl_import.cc + importer/stl_import_mesh.cc IO_stl.h - importer/stl_import_mesh.hh + importer/stl_import.hh importer/stl_import_ascii_reader.hh importer/stl_import_binary_reader.hh - importer/stl_import.hh + importer/stl_import_mesh.hh ) set(LIB diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index f65657b240c..103bb0d0cef 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -82,9 +82,8 @@ static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim) return mtl; } -/* Returns an existing Blender material that corresponds to the USD - * material with with the given path. Returns null if no such material - * exists. */ +/* Returns an existing Blender material that corresponds to the USD material with the given path. + * Returns null if no such material exists. */ static Material *find_existing_material( const pxr::SdfPath &usd_mat_path, const USDImportParams ¶ms, diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc index fb0b4a1aca9..2fd2973ee73 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc @@ -4,6 +4,7 @@ * \ingroup obj */ +#include "BLI_path_util.h" #include "BLI_timeit.hh" #include "IO_wavefront_obj.h" @@ -11,14 +12,26 @@ #include "obj_exporter.hh" #include "obj_importer.hh" +using namespace blender::timeit; + +static void report_duration(const char *job, const TimePoint &start_time, const char *path) +{ + Nanoseconds duration = Clock::now() - start_time; + std::cout << "OBJ " << job << " of '" << BLI_path_basename(path) << "' took "; + print_duration(duration); + std::cout << '\n'; +} + void OBJ_export(bContext *C, const OBJExportParams *export_params) { - SCOPED_TIMER("OBJ export"); + TimePoint start_time = Clock::now(); blender::io::obj::exporter_main(C, *export_params); + report_duration("export", start_time, export_params->filepath); } void OBJ_import(bContext *C, const OBJImportParams *import_params) { - SCOPED_TIMER(__func__); + TimePoint start_time = Clock::now(); blender::io::obj::importer_main(C, *import_params); + report_duration("import", start_time, import_params->filepath); } diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 6ad96083e37..847b02d3fd1 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -75,6 +75,7 @@ struct OBJImportParams { bool import_vertex_groups; bool validate_meshes; bool relative_paths; + bool clear_selection; }; /** diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 36a9cf1b9ae..53aa80700cc 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -547,20 +547,29 @@ StringRefNull MTLWriter::mtl_file_path() const return mtl_filepath_; } -void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material) +void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl) { - fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl_material.Ns); - fmt_handler_.write<eMTLSyntaxElement::Ka>( - mtl_material.Ka.x, mtl_material.Ka.y, mtl_material.Ka.z); - fmt_handler_.write<eMTLSyntaxElement::Kd>( - mtl_material.Kd.x, mtl_material.Kd.y, mtl_material.Kd.z); - fmt_handler_.write<eMTLSyntaxElement::Ks>( - mtl_material.Ks.x, mtl_material.Ks.y, mtl_material.Ks.z); - fmt_handler_.write<eMTLSyntaxElement::Ke>( - mtl_material.Ke.x, mtl_material.Ke.y, mtl_material.Ke.z); - fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl_material.Ni); - fmt_handler_.write<eMTLSyntaxElement::d>(mtl_material.d); - fmt_handler_.write<eMTLSyntaxElement::illum>(mtl_material.illum); + /* For various material properties, we only capture information + * coming from the texture, or the default value of the socket. + * When the texture is present, do not emit the default value. */ + if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ns).is_valid()) { + fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl.Ns); + } + fmt_handler_.write<eMTLSyntaxElement::Ka>(mtl.Ka.x, mtl.Ka.y, mtl.Ka.z); + if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Kd).is_valid()) { + fmt_handler_.write<eMTLSyntaxElement::Kd>(mtl.Kd.x, mtl.Kd.y, mtl.Kd.z); + } + if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ks).is_valid()) { + fmt_handler_.write<eMTLSyntaxElement::Ks>(mtl.Ks.x, mtl.Ks.y, mtl.Ks.z); + } + if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_Ke).is_valid()) { + fmt_handler_.write<eMTLSyntaxElement::Ke>(mtl.Ke.x, mtl.Ke.y, mtl.Ke.z); + } + fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl.Ni); + if (!mtl.tex_map_of_type(eMTLSyntaxElement::map_d).is_valid()) { + fmt_handler_.write<eMTLSyntaxElement::d>(mtl.d); + } + fmt_handler_.write<eMTLSyntaxElement::illum>(mtl.illum); } void MTLWriter::write_texture_map( @@ -583,12 +592,13 @@ void MTLWriter::write_texture_map( options.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength)); } + std::string path = path_reference( + texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, ©_set); + /* Always emit forward slashes for cross-platform compatibility. */ + std::replace(path.begin(), path.end(), '\\', '/'); + #define SYNTAX_DISPATCH(eMTLSyntaxElement) \ if (texture_map.key == eMTLSyntaxElement) { \ - std::string path = path_reference( \ - texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, ©_set); \ - /* Always emit forward slashes for cross-platform compatibility. */ \ - std::replace(path.begin(), path.end(), '\\', '/'); \ fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \ return; \ } @@ -600,6 +610,7 @@ void MTLWriter::write_texture_map( SYNTAX_DISPATCH(eMTLSyntaxElement::map_refl); SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ke); SYNTAX_DISPATCH(eMTLSyntaxElement::map_Bump); +#undef SYNTAX_DISPATCH BLI_assert(!"This map type was not written to the file."); } @@ -626,7 +637,7 @@ void MTLWriter::write_materials(const char *blen_filepath, fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name); write_bsdf_properties(mtlmat); for (const auto &tex : mtlmat.texture_maps.items()) { - if (tex.value.image_path.empty()) { + if (!tex.value.is_valid()) { continue; } write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set); diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index 9460746630d..9b050af0891 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -296,7 +296,7 @@ void OBJMesh::store_uv_coords_and_indices() const float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT}; UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create( - mpoly, mloop, mloopuv, totpoly, totvert, limit, false, false); + mpoly, nullptr, mloop, mloopuv, totpoly, totvert, limit, false, false); uv_indices_.resize(totpoly); /* At least total vertices of a mesh will be present in its texture map. So diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh index 80e3127f69f..f83b3b49bf5 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh @@ -8,8 +8,6 @@ #include "BLI_map.hh" #include "BLI_math_vec_types.hh" -#include "BLI_string_ref.hh" -#include "BLI_vector.hh" #include "DNA_node_types.h" #include "obj_export_io.hh" @@ -25,19 +23,22 @@ template<> struct DefaultHash<io::obj::eMTLSyntaxElement> { } // namespace blender namespace blender::io::obj { -class OBJMesh; /** * Generic container for texture node properties. */ struct tex_map_XX { tex_map_XX(StringRef to_socket_id) : dest_socket_id(to_socket_id){}; + bool is_valid() const + { + return !image_path.empty(); + } - /** Target socket which this texture node connects to. */ + /* Target socket which this texture node connects to. */ const std::string dest_socket_id; float3 translation{0.0f}; float3 scale{1.0f}; - /* Only Flat and Smooth projections are supported. */ + /* Only Flat and Sphere projections are supported. */ int projection_type = SHD_PROJ_FLAT; std::string image_path; std::string mtl_dir_path; @@ -58,16 +59,15 @@ struct MTLMaterial { texture_maps.add(eMTLSyntaxElement::map_Bump, tex_map_XX("Normal")); } - /** - * Caller must ensure that the given lookup key exists in the Map. - * \return Texture map corresponding to the given ID. - */ + const tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key) const + { + BLI_assert(texture_maps.contains(key)); + return texture_maps.lookup(key); + } tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key) { - { - BLI_assert(texture_maps.contains_as(key)); - return texture_maps.lookup_as(key); - } + BLI_assert(texture_maps.contains(key)); + return texture_maps.lookup(key); } std::string name; diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index 8594603867f..7069e1185e0 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -406,8 +406,7 @@ static void use_all_vertices_if_no_faces(Geometry *geom, all_geometries.begin(), all_geometries.end(), [](const std::unique_ptr<Geometry> &g) { return g->get_vertex_count() == 0; })) { - geom->track_vertex_index(0); - geom->track_vertex_index(global_vertices.vertices.size() - 1); + geom->track_all_vertices(global_vertices.vertices.size()); } } } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index d7b2bc2e67c..e62470588ec 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -157,17 +157,17 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - const int tot_verts_object{mesh_geometry_.get_vertex_count()}; - for (int i = 0; i < tot_verts_object; ++i) { - int vi = mesh_geometry_.vertex_index_min_ + i; + int mi = 0; + for (int vi : mesh_geometry_.vertices_) { if (vi < global_vertices_.vertices.size()) { - copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]); + copy_v3_v3(mesh->mvert[mi].co, global_vertices_.vertices[vi]); } else { std::cerr << "Vertex index:" << vi << " larger than total vertices:" << global_vertices_.vertices.size() << " ." << std::endl; } + ++mi; } } @@ -208,7 +208,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; MLoop &mloop = mesh->mloop[tot_loop_idx]; tot_loop_idx++; - mloop.v = curr_corner.vert_index - mesh_geometry_.vertex_index_min_; + mloop.v = mesh_geometry_.global_to_local_vertices_.lookup_default(curr_corner.vert_index, 0); /* Setup vertex group data, if needed. */ if (!mesh->dvert) { @@ -240,8 +240,8 @@ void MeshFromGeometry::create_edges(Mesh *mesh) for (int i = 0; i < tot_edges; ++i) { const MEdge &src_edge = mesh_geometry_.edges_[i]; MEdge &dst_edge = mesh->medge[i]; - dst_edge.v1 = src_edge.v1 - mesh_geometry_.vertex_index_min_; - dst_edge.v2 = src_edge.v2 - mesh_geometry_.vertex_index_min_; + dst_edge.v1 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v1, 0); + dst_edge.v2 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v2, 0); BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts); dst_edge.flag = ME_LOOSEEDGE; } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index 0023d1159c5..02e09a77a5d 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -134,6 +134,14 @@ static Image *load_texture_image(Main *bmain, return image; } } + /* Try taking just the basename from input path. */ + std::string base_path{tex_map.mtl_dir_path + BLI_path_basename(tex_map.image_path.c_str())}; + if (base_path != tex_path) { + image = load_image_at_path(bmain, base_path, relative_paths); + if (image != nullptr) { + return image; + } + } image = create_placeholder_image(bmain, tex_path); return image; @@ -334,7 +342,7 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat) if (emission_color.x >= 0 && emission_color.y >= 0 && emission_color.z >= 0) { set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf_); } - if (mtl_mat_.texture_maps.contains_as(eMTLSyntaxElement::map_Ke)) { + if (mtl_mat_.tex_map_of_type(eMTLSyntaxElement::map_Ke).is_valid()) { set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf_); } set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf_); @@ -357,7 +365,7 @@ void ShaderNodetreeWrap::add_image_textures(Main *bmain, Material *mat, bool rel { for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item texture_map : mtl_mat_.texture_maps.items()) { - if (texture_map.value.image_path.empty()) { + if (!texture_map.value.is_valid()) { /* No Image texture node of this map type can be added to this material. */ continue; } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh index 9f0079d7c53..f48b6dd55e8 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -9,6 +9,7 @@ #include "BKE_lib_id.h" #include "BLI_map.hh" +#include "BLI_math_base.hh" #include "BLI_math_vec_types.hh" #include "BLI_vector.hh" #include "BLI_vector_set.hh" @@ -92,6 +93,8 @@ struct Geometry { int vertex_index_min_ = INT_MAX; int vertex_index_max_ = -1; + VectorSet<int> vertices_; + Map<int, int> global_to_local_vertices_; /** Edges written in the file in addition to (or even without polygon) elements. */ Vector<MEdge> edges_; @@ -105,14 +108,26 @@ struct Geometry { int get_vertex_count() const { - if (vertex_index_max_ < vertex_index_min_) - return 0; - return vertex_index_max_ - vertex_index_min_ + 1; + return (int)vertices_.size(); } void track_vertex_index(int index) { - vertex_index_min_ = std::min(vertex_index_min_, index); - vertex_index_max_ = std::max(vertex_index_max_, index); + if (vertices_.add(index)) { + global_to_local_vertices_.add_new(index, (int)vertices_.size() - 1); + } + math::min_inplace(vertex_index_min_, index); + math::max_inplace(vertex_index_max_, index); + } + void track_all_vertices(int count) + { + vertices_.reserve(count); + global_to_local_vertices_.reserve(count); + for (int i = 0; i < count; ++i) { + vertices_.add(i); + global_to_local_vertices_.add(i, i); + } + vertex_index_min_ = 0; + vertex_index_max_ = count - 1; } }; diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc index bb32776d2be..5d3f75e7f38 100644 --- a/source/blender/io/wavefront_obj/importer/obj_importer.cc +++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc @@ -39,7 +39,6 @@ static void geometry_to_blender_objects(Main *bmain, Map<std::string, std::unique_ptr<MTLMaterial>> &materials, Map<std::string, Material *> &created_materials) { - BKE_view_layer_base_deselect_all(view_layer); LayerCollection *lc = BKE_layer_collection_get_active(view_layer); /* Don't do collection syncs for each object, will do once after the loop. */ @@ -122,6 +121,9 @@ void importer_main(Main *bmain, mtl_parser.parse_and_store(materials); } + if (import_params.clear_selection) { + BKE_view_layer_base_deselect_all(view_layer); + } geometry_to_blender_objects(bmain, scene, view_layer, diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 339f672c3e0..132bb03357f 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -62,6 +62,7 @@ class obj_importer_test : public BlendfileLoadingBaseTest { params.validate_meshes = true; params.import_vertex_groups = false; params.relative_paths = true; + params.clear_selection = true; std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path; strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1); @@ -153,7 +154,7 @@ TEST_F(obj_importer_test, import_cube) 12, 6, 24, - float3(-1, -1, 1), + float3(1, -1, 1), float3(1, -1, -1), float3(-0.57735f, 0.57735f, -0.57735f)}, }; @@ -171,19 +172,19 @@ TEST_F(obj_importer_test, import_cube_o_after_verts) 12, 6, 24, - float3(-1, -1, 1), + float3(1, -1, 1), float3(1, -1, -1), float3(0, 0, 1), }, { "OBSparseTri", OB_MESH, - 6, + 3, 3, 1, 3, - float3(1, -1, 1), float3(-2, -2, 2), + float3(-1, -1, -1), float3(-0.2357f, 0.9428f, 0.2357f), }, }; @@ -200,8 +201,8 @@ TEST_F(obj_importer_test, import_suzanne_all_data) 1005, 500, 1968, - float3(-0.4375f, 0.164062f, 0.765625f), - float3(0.4375f, 0.164062f, 0.765625f), + float3(-0.5f, 0.09375f, 0.6875f), + float3(0.546875f, 0.054688f, 0.578125f), float3(-0.6040f, -0.5102f, 0.6122f), float2(0.692094f, 0.40191f)}, }; @@ -306,7 +307,7 @@ TEST_F(obj_importer_test, import_materials) { Expectation expect[] = { {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, - {"OBmaterials", OB_MESH, 8, 12, 6, 24, float3(-1, -1, 1), float3(1, -1, -1)}, + {"OBmaterials", OB_MESH, 8, 12, 6, 24, float3(1, -1, 1), float3(1, -1, -1)}, }; import_and_check("materials.obj", expect, std::size(expect), 4, 8); } @@ -322,7 +323,17 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel) 6, 24, float3(1, 1, -1), - float3(-1, -1, 1), + float3(1, -1, -1), + float3(0, 1, 0), + float2(0.9935f, 0.0020f)}, + {"OBCubeTexMul", + OB_MESH, + 8, + 12, + 6, + 24, + float3(4, -2, -1), + float3(4, -4, -1), float3(0, 1, 0), float2(0.9935f, 0.0020f)}, {"OBCubeTiledTex", @@ -332,7 +343,7 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel) 6, 24, float3(4, 1, -1), - float3(2, -1, 1), + float3(4, -1, -1), float3(0, 1, 0), float2(0.9935f, 0.0020f)}, {"OBCubeTiledTexFromAnotherFolder", @@ -342,11 +353,11 @@ TEST_F(obj_importer_test, import_cubes_with_textures_rel) 6, 24, float3(7, 1, -1), - float3(5, -1, 1), + float3(7, -1, -1), float3(0, 1, 0), float2(0.9935f, 0.0020f)}, }; - import_and_check("cubes_with_textures_rel.obj", expect, std::size(expect), 3, 4); + import_and_check("cubes_with_textures_rel.obj", expect, std::size(expect), 4, 4); } TEST_F(obj_importer_test, import_faces_invalid_or_with_holes) @@ -433,15 +444,15 @@ TEST_F(obj_importer_test, import_all_objects) {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, /* .obj file has empty EmptyText and EmptyMesh objects; these are ignored and skipped */ {"OBBezierCurve", OB_MESH, 13, 12, 0, 0, float3(-1, -2, 0), float3(1, -2, 0)}, - {"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, -1), float3(-1, 1, 1), float3(0, 0, 1)}, + {"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, 1), float3(-1, 1, -1), float3(0, 0, 1)}, {"OBMaterialCube", OB_MESH, 8, 13, 7, 26, - float3(28, 1, -1), - float3(26, 1, 1), + float3(26, -1, -1), + float3(28, -1, -1), float3(-1, 0, 0)}, {"OBNurbsCircle", OB_MESH, @@ -451,15 +462,15 @@ TEST_F(obj_importer_test, import_all_objects) 0, float3(3.292893f, -2.707107f, 0), float3(3.369084f, -2.77607f, 0)}, - {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(2, -3, 0), float3(3, -2, 0)}, + {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(3, -2, 0), float3(2, -1, 0)}, {"OBParticleCube", OB_MESH, 8, 13, 7, 26, - float3(22, 1, -1), - float3(20, 1, 1), + float3(22, 1, 1), + float3(20, 1, -1), float3(0, 0, 1)}, {"OBShapeKeyCube", OB_MESH, @@ -467,8 +478,8 @@ TEST_F(obj_importer_test, import_all_objects) 13, 7, 26, - float3(19, 1, -1), - float3(17, 1, 1), + float3(19, 1, 2), + float3(17, 1, -1), float3(-0.4082f, -0.4082f, 0.8165f)}, {"OBSmoothCube", OB_MESH, @@ -476,8 +487,8 @@ TEST_F(obj_importer_test, import_all_objects) 13, 7, 26, - float3(4, 1, -1), - float3(2, 1, 1), + float3(4, 1, 1), + float3(2, 1, -1), float3(0.5774f, 0.5773f, 0.5774f)}, {"OBSurface", OB_MESH, @@ -485,7 +496,7 @@ TEST_F(obj_importer_test, import_all_objects) 480, 224, 896, - float3(7.292893f, -2.707107f, -1), + float3(7.292893f, -2.707107f, -0.714285f), float3(7.525872f, -2.883338f, 1), float3(-0.7071f, -0.7071f, 0), float2(0, 0.142857f)}, @@ -495,7 +506,7 @@ TEST_F(obj_importer_test, import_all_objects) 480, 225, 900, - float3(12.5f, -2.5f, 0.694444f), + float3(12.56667f, -2.5f, 0.72037f), float3(13.5f, -1.5f, 0.694444f), float3(-0.3246f, -0.3531f, 0.8775f), float2(0, 0.066667f)}, @@ -516,7 +527,7 @@ TEST_F(obj_importer_test, import_all_objects) 1024, 4096, float3(5.34467f, -2.65533f, -0.176777f), - float3(5.232792f, -2.411795f, -0.220835f), + float3(5.158205f, -2.234695f, -0.220835f), float3(-0.5042f, -0.5042f, -0.7011f), float2(0, 1)}, {"OBTaperCube", @@ -525,8 +536,8 @@ TEST_F(obj_importer_test, import_all_objects) 208, 104, 416, - float3(24.444445f, 0.502543f, -0.753814f), - float3(23.790743f, 0.460522f, -0.766546f), + float3(24.316156f, 0.345556f, 0.796778f), + float3(23.551804f, 0.389113f, -0.639607f), float3(-0.0546f, 0.1716f, 0.9837f)}, {"OBText", OB_MESH, @@ -534,8 +545,8 @@ TEST_F(obj_importer_test, import_all_objects) 345, 171, 513, - float3(1.75f, -9.458f, 0), - float3(0.587f, -9.406f, 0), + float3(1.583f, -9.621f, 0), + float3(0.351f, -10.0f, 0), float3(0, 0, 1), float2(0.017544f, 0)}, {"OBUVCube", @@ -544,8 +555,8 @@ TEST_F(obj_importer_test, import_all_objects) 13, 7, 26, - float3(7, 1, -1), - float3(5, 1, 1), + float3(7, 1, 1), + float3(5, 1, -1), float3(0, 0, 1), float2(0.654526f, 0.579873f)}, {"OBUVImageCube", @@ -554,8 +565,8 @@ TEST_F(obj_importer_test, import_all_objects) 13, 7, 26, - float3(10, 1, -1), - float3(8, 1, 1), + float3(10, 1, 1), + float3(8, 1, -1), float3(0, 0, 1), float2(0.654526f, 0.579873f)}, {"OBVColCube", @@ -564,8 +575,8 @@ TEST_F(obj_importer_test, import_all_objects) 13, 7, 26, - float3(13, 1, -1), - float3(11, 1, 1), + float3(13, 1, 1), + float3(11, 1, -1), float3(0, 0, 1), float2(0, 0), float4(0.0f, 0.002125f, 1.0f, 1.0f)}, @@ -575,8 +586,8 @@ TEST_F(obj_importer_test, import_all_objects) 13, 7, 26, - float3(16, 1, -1), - float3(14, 1, 1), + float3(16, 1, 1), + float3(14, 1, -1), float3(0, 0, 1)}, }; import_and_check("all_objects.obj", expect, std::size(expect), 7); @@ -593,7 +604,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors) 6, 24, float3(1.0f, 1.0f, -3.812445f), - float3(-1.0f, -1.0f, -1.812445f), + float3(1.0f, -1.0f, -3.812445f), float3(0, 0, 0), float2(0, 0), float4(0.89627f, 0.036889f, 0.47932f, 1.0f)}, @@ -604,7 +615,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors) 6, 24, float3(3.481967f, 1.0f, -3.812445f), - float3(1.481967f, -1.0f, -1.812445f), + float3(3.481967f, -1.0f, -3.812445f), float3(0, 0, 0), float2(0, 0), float4(1.564582f, 0.039217f, 0.664309f, 1.0f)}, @@ -615,7 +626,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors) 6, 24, float3(-4.725068f, -1.0f, 1.0f), - float3(-2.725068f, 1.0f, -1.0f), + float3(-2.725068f, -1.0f, 1.0f), float3(0, 0, 0), float2(0, 0), float4(0.270498f, 0.47932f, 0.262251f, 1.0f)}, @@ -626,7 +637,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors) 6, 24, float3(-4.550208f, -1.0f, -1.918042f), - float3(-2.550208f, 1.0f, -3.918042f)}, + float3(-2.550208f, -1.0f, -1.918042f)}, {"OBCubeVertexByte", OB_MESH, 8, @@ -634,7 +645,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors) 6, 24, float3(1.0f, 1.0f, -1.0f), - float3(-1.0f, -1.0f, 1.0f), + float3(1.0f, -1.0f, -1.0f), float3(0, 0, 0), float2(0, 0), float4(0.846873f, 0.027321f, 0.982123f, 1.0f)}, @@ -645,7 +656,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors) 6, 24, float3(3.392028f, 1.0f, -1.0f), - float3(1.392028f, -1.0f, 1.0f), + float3(3.392028f, -1.0f, -1.0f), float3(0, 0, 0), float2(0, 0), float4(49.99467f, 0.027321f, 0.982123f, 1.0f)}, @@ -664,7 +675,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb) 6, 24, float3(4, 1, -1), - float3(2, -1, 1), + float3(4, -1, -1), float3(0, 0, 0), float2(0, 0), float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}, @@ -675,7 +686,7 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb) 6, 24, float3(1, 1, -1), - float3(-1, -1, 1), + float3(1, -1, -1), float3(0, 0, 0), float2(0, 0), float4(0.6038f, 0.3185f, 0.1329f, 1.0f)}, diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index c8081c49ca4..209ffd3342d 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -34,10 +34,13 @@ typedef struct MVert { } MVert; /** #MVert.flag */ + +#ifdef DNA_DEPRECATED_ALLOW enum { /* SELECT = (1 << 0), */ ME_HIDE = (1 << 4), }; +#endif /** * Mesh Edges. @@ -359,7 +362,7 @@ typedef struct MDisps { /** * Used for hiding parts of a multires mesh. - * Essentially the multires equivalent of #MVert.flag's ME_HIDE bit. + * Essentially the multires equivalent of the mesh ".hide_vert" boolean layer. * * \note This is a bitmap, keep in sync with type used in BLI_bitmap.h */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index f148116eba8..787f52f9891 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2217,7 +2217,7 @@ typedef struct SurfaceDeformModifierData { SDefVert *verts; void *_pad1; float falloff; - /* Number of of vertices on the deformed mesh upon the bind process. */ + /* Number of vertices on the deformed mesh upon the bind process. */ unsigned int mesh_verts_num; /* Number of vertices in the `verts` array of this modifier. */ unsigned int bind_verts_num; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 76d8207eead..b9161e918c0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -632,12 +632,12 @@ typedef struct bNodeSocketValueMaterial { /* Data structs, for `node->storage`. */ -enum { +typedef enum CMPNodeMaskType { CMP_NODE_MASKTYPE_ADD = 0, CMP_NODE_MASKTYPE_SUBTRACT = 1, CMP_NODE_MASKTYPE_MULTIPLY = 2, CMP_NODE_MASKTYPE_NOT = 3, -}; +} CMPNodeMaskType; enum { CMP_NODE_DILATEERODE_STEP = 0, @@ -1838,6 +1838,49 @@ enum { /* viewer and composite output. */ #define CMP_NODE_OUTPUT_IGNORE_ALPHA 1 +/** Split Viewer Node. Stored in `custom2`. */ +typedef enum CMPNodeSplitViewerAxis { + CMP_NODE_SPLIT_VIEWER_HORIZONTAL = 0, + CMP_NODE_SPLIT_VIEWER_VERTICAL = 1, +} CMPNodeSplitViewerAxis; + +/** Color Balance Node. Stored in `custom1`. */ +typedef enum CMPNodeColorBalanceMethod { + CMP_NODE_COLOR_BALANCE_LGG = 0, + CMP_NODE_COLOR_BALANCE_ASC_CDL = 1, +} CMPNodeColorBalanceMethod; + +/** Alpha Convert Node. Stored in `custom1`. */ +typedef enum CMPNodeAlphaConvertMode { + CMP_NODE_ALPHA_CONVERT_PREMULTIPLY = 0, + CMP_NODE_ALPHA_CONVERT_UNPREMULTIPLY = 1, +} CMPNodeAlphaConvertMode; + +/** Distance Matte Node. Stored in #NodeChroma.channel. */ +typedef enum CMPNodeDistanceMatteColorSpace { + CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_YCCA = 0, + CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA = 1, +} CMPNodeDistanceMatteColorSpace; + +/** Color Spill Node. Stored in `custom2`. */ +typedef enum CMPNodeColorSpillLimitAlgorithm { + CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE = 0, + CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE = 1, +} CMPNodeColorSpillLimitAlgorithm; + +/** Channel Matte Node. Stored in #NodeChroma.algorithm. */ +typedef enum CMPNodeChannelMatteLimitAlgorithm { + CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE = 0, + CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX = 1, +} CMPNodeChannelMatteLimitAlgorithm; + +/* Flip Node. Stored in custom1. */ +typedef enum CMPNodeFlipMode { + CMP_NODE_FLIP_X = 0, + CMP_NODE_FLIP_Y = 1, + CMP_NODE_FLIP_X_Y = 2, +} CMPNodeFlipMode; + /* Plane track deform node. */ enum { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f8fcd78d63b..cfb077748ef 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -9,7 +9,7 @@ #include "DNA_defs.h" -/* XXX(campbell): temp feature. */ +/* XXX(@campbellbarton): temp feature. */ #define DURIAN_CAMERA_SWITCH /* check for cyclic set-scene, diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 74fb1c3ac96..ac553c2655f 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -653,6 +653,8 @@ typedef struct UserDef_Experimental { char enable_eevee_next; char use_sculpt_texture_paint; char use_draw_manager_acquire_lock; + char use_realtime_compositor; + char _pad[7]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 8554d070dc3..0d281032b7e 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -486,6 +486,7 @@ enum { V3D_SHADING_SCENE_LIGHTS_RENDER = (1 << 12), V3D_SHADING_SCENE_WORLD_RENDER = (1 << 13), V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION = (1 << 14), + V3D_SHADING_COMPOSITOR = (1 << 15), }; #define V3D_USES_SCENE_LIGHTS(v3d) \ diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 4b8f8ca7b4b..c59e45e1b01 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -234,6 +234,10 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { # include "WM_api.h" +# ifdef WITH_PYTHON +# include "BPY_extern.h" +# endif + void rna_ID_override_library_property_operation_refname_get(PointerRNA *ptr, char *value) { IDOverrideLibraryPropertyOperation *opop = ptr->data; @@ -695,7 +699,16 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, true); } - ID *local_id = BKE_lib_override_library_create_from_id(bmain, id, remap_local_usages); + ID *local_id = NULL; +# ifdef WITH_PYTHON + BPy_BEGIN_ALLOW_THREADS; +# endif + + local_id = BKE_lib_override_library_create_from_id(bmain, id, remap_local_usages); + +# ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +# endif if (remap_local_usages) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); @@ -717,9 +730,18 @@ static ID *rna_ID_override_hierarchy_create( BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); ID *id_root_override = NULL; + +# ifdef WITH_PYTHON + BPy_BEGIN_ALLOW_THREADS; +# endif + BKE_lib_override_library_create( bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override, false); +# ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +# endif + WM_main_add_notifier(NC_ID | NA_ADDED, NULL); WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); @@ -2158,7 +2180,7 @@ static void rna_def_ID(BlenderRNA *brna) func = RNA_def_function(srna, "animation_data_clear", "rna_ID_animation_data_free"); RNA_def_function_flag(func, FUNC_USE_MAIN); - RNA_def_function_ui_description(func, "Clear animation on this this ID"); + RNA_def_function_ui_description(func, "Clear animation on this ID"); func = RNA_def_function(srna, "update_tag", "rna_ID_update_tag"); RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 04707c01d6b..ada73157026 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2073,7 +2073,7 @@ static void rna_property_update( } #if 1 - /* TODO(campbell): Should eventually be replaced entirely by message bus (below) + /* TODO(@campbellbarton): Should eventually be replaced entirely by message bus (below) * for now keep since COW, bugs are hard to track when we have other missing updates. */ if (prop->noteflag) { WM_main_add_notifier(prop->noteflag, ptr->owner_id); diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index bcfb646ca19..14f4a82c62b 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -177,7 +177,7 @@ static void rna_Action_fcurve_clear(bAction *act) static TimeMarker *rna_Action_pose_markers_new(bAction *act, const char name[]) { TimeMarker *marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker"); - marker->flag = 1; + marker->flag = SELECT; marker->frame = 1; BLI_strncpy_utf8(marker->name, name, sizeof(marker->name)); BLI_addtail(&act->markers, marker); diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 99f8c263da6..988e65b4ba8 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -695,14 +695,12 @@ void RNA_def_camera(BlenderRNA *brna) prop = RNA_def_property(srna, "shift_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "shiftx"); - RNA_def_property_range(prop, -10.0f, 10.0f); RNA_def_property_ui_range(prop, -2.0, 2.0, 1, 3); RNA_def_property_ui_text(prop, "Shift X", "Camera horizontal shift"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); prop = RNA_def_property(srna, "shift_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "shifty"); - RNA_def_property_range(prop, -10.0f, 10.0f); RNA_def_property_ui_range(prop, -2.0, 2.0, 1, 3); RNA_def_property_ui_text(prop, "Shift Y", "Camera vertical shift"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index cb8b36f41d2..17290d1c582 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -195,7 +195,7 @@ static void rna_def_curves_point(BlenderRNA *brna) PropertyRNA *prop; srna = RNA_def_struct(brna, "CurvePoint", NULL); - RNA_def_struct_ui_text(srna, "Curve Point", "Curve curve control point"); + RNA_def_struct_ui_text(srna, "Curve Point", "Curve control point"); RNA_def_struct_path_func(srna, "rna_CurvePoint_path"); prop = RNA_def_property(srna, "position", PROP_FLOAT, PROP_TRANSLATION); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 22a43f1df8f..63d8d572c5f 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -352,19 +352,93 @@ static void rna_Mesh_update_positions_tag(Main *bmain, Scene *scene, PointerRNA /** \name Property get/set Callbacks * \{ */ +<<<<<<< HEAD static int rna_MeshVertex_index_get(PointerRNA *ptr); static int rna_MeshEdge_index_get(PointerRNA *ptr); +======= +static int rna_MeshVertex_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MVert *vert = (MVert *)ptr->data; + const int index = (int)(vert - mesh->mvert); + BLI_assert(index >= 0); + BLI_assert(index < mesh->totvert); + return index; +} + +static int rna_MeshEdge_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MEdge *edge = (MEdge *)ptr->data; + const int index = (int)(edge - mesh->medge); + BLI_assert(index >= 0); + BLI_assert(index < mesh->totedge); + return index; +} + +static int rna_MeshPolygon_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MPoly *mpoly = (MPoly *)ptr->data; + const int index = (int)(mpoly - mesh->mpoly); + BLI_assert(index >= 0); + BLI_assert(index < mesh->totpoly); + return index; +} + +static int rna_MeshLoop_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MLoop *mloop = (MLoop *)ptr->data; + const int index = (int)(mloop - mesh->mloop); + BLI_assert(index >= 0); + BLI_assert(index < mesh->totloop); + return index; +} + +static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MLoopTri *ltri = (MLoopTri *)ptr->data; + const int index = (int)(ltri - mesh->runtime.looptris.array); + BLI_assert(index >= 0); + BLI_assert(index < mesh->runtime.looptris.len); + return index; +} +>>>>>>> master static void rna_MeshVertex_normal_get(PointerRNA *ptr, float *value) { Mesh *mesh = rna_mesh(ptr); const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh); + const int index = rna_MeshVertex_index_get(ptr); + copy_v3_v3(value, vert_normals[index]); +} - const int index = (MVert *)ptr->data - mesh->mvert; - BLI_assert(index >= 0); - BLI_assert(index < mesh->totvert); +static bool rna_MeshVertex_hide_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const bool *hide_vert = (const bool *)CustomData_get_layer_named( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + const int index = rna_MeshVertex_index_get(ptr); + return hide_vert == NULL ? false : hide_vert[index]; +} - copy_v3_v3(value, vert_normals[index]); +static void rna_MeshVertex_hide_set(PointerRNA *ptr, bool value) +{ + Mesh *mesh = rna_mesh(ptr); + bool *hide_vert = (bool *)CustomData_duplicate_referenced_layer_named( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert); + if (!hide_vert) { + if (!value) { + /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */ + return; + } + hide_vert = (bool *)CustomData_add_layer_named( + &mesh->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totvert, ".hide_vert"); + } + const int index = rna_MeshVertex_index_get(ptr); + hide_vert[index] = value; } static float rna_MeshVertex_bevel_weight_get(PointerRNA *ptr) @@ -483,6 +557,32 @@ static void rna_MeshPolygon_normal_get(PointerRNA *ptr, float *values) BKE_mesh_calc_poly_normal(mp, me->mloop + mp->loopstart, me->mvert, values); } +static bool rna_MeshPolygon_hide_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly"); + const int index = rna_MeshPolygon_index_get(ptr); + return hide_poly == NULL ? false : hide_poly[index]; +} + +static void rna_MeshPolygon_hide_set(PointerRNA *ptr, bool value) +{ + Mesh *mesh = rna_mesh(ptr); + bool *hide_poly = (bool *)CustomData_duplicate_referenced_layer_named( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); + if (!hide_poly) { + if (!value) { + /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */ + return; + } + hide_poly = (bool *)CustomData_add_layer_named( + &mesh->pdata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totpoly, ".hide_poly"); + } + const int index = rna_MeshPolygon_index_get(ptr); + hide_poly[index] = value; +} + static void rna_MeshPolygon_center_get(PointerRNA *ptr, float *values) { Mesh *me = rna_mesh(ptr); @@ -1193,55 +1293,46 @@ static void rna_MeshPoly_material_index_range( } # endif -static int rna_MeshVertex_index_get(PointerRNA *ptr) +static bool rna_MeshEdge_hide_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MVert *vert = (MVert *)ptr->data; - return (int)(vert - me->mvert); + const Mesh *mesh = rna_mesh(ptr); + const bool *hide_edge = (const bool *)CustomData_get_layer_named( + &mesh->edata, CD_PROP_BOOL, ".hide_edge"); + const int index = rna_MeshEdge_index_get(ptr); + return hide_edge == NULL ? false : hide_edge[index]; } -static int rna_MeshEdge_index_get(PointerRNA *ptr) +static void rna_MeshEdge_hide_set(PointerRNA *ptr, bool value) { - Mesh *me = rna_mesh(ptr); - MEdge *edge = (MEdge *)ptr->data; - return (int)(edge - me->medge); -} - -static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr) -{ - Mesh *me = rna_mesh(ptr); - MLoopTri *ltri = (MLoopTri *)ptr->data; - return (int)(ltri - me->runtime.looptris.array); + Mesh *mesh = rna_mesh(ptr); + bool *hide_edge = (bool *)CustomData_duplicate_referenced_layer_named( + &mesh->edata, CD_PROP_BOOL, ".hide_edge", mesh->totedge); + if (!hide_edge) { + if (!value) { + /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */ + return; + } + hide_edge = (bool *)CustomData_add_layer_named( + &mesh->edata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totedge, ".hide_edge"); + } + const int index = rna_MeshEdge_index_get(ptr); + hide_edge[index] = value; } static int rna_MeshLoopTriangle_material_index_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MLoopTri *ltri = (MLoopTri *)ptr->data; + const Mesh *me = rna_mesh(ptr); + const MLoopTri *ltri = (MLoopTri *)ptr->data; return me->mpoly[ltri->poly].mat_nr; } static bool rna_MeshLoopTriangle_use_smooth_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MLoopTri *ltri = (MLoopTri *)ptr->data; + const Mesh *me = rna_mesh(ptr); + const MLoopTri *ltri = (MLoopTri *)ptr->data; return me->mpoly[ltri->poly].flag & ME_SMOOTH; } -static int rna_MeshPolygon_index_get(PointerRNA *ptr) -{ - Mesh *me = rna_mesh(ptr); - MPoly *mpoly = (MPoly *)ptr->data; - return (int)(mpoly - me->mpoly); -} - -static int rna_MeshLoop_index_get(PointerRNA *ptr) -{ - Mesh *me = rna_mesh(ptr); - MLoop *mloop = (MLoop *)ptr->data; - return (int)(mloop - me->mloop); -} - /* path construction */ static char *rna_VertexGroupElement_path(const PointerRNA *ptr) @@ -1849,8 +1940,8 @@ static void rna_def_mvert(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE); RNA_def_property_ui_text(prop, "Hide", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshVertex_hide_get", "rna_MeshVertex_hide_set"); RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "bevel_weight", PROP_FLOAT, PROP_NONE); @@ -1925,8 +2016,8 @@ static void rna_def_medge(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE); RNA_def_property_ui_text(prop, "Hide", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshEdge_hide_get", "rna_MeshEdge_hide_set"); RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "use_seam", PROP_BOOLEAN, PROP_NONE); @@ -2138,8 +2229,8 @@ static void rna_def_mpolygon(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE); RNA_def_property_ui_text(prop, "Hide", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshPolygon_hide_get", "rna_MeshPolygon_hide_set"); RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 24ba8f0fd30..7d2fa8022dd 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -6477,7 +6477,7 @@ static void def_sh_script(StructRNA *srna) RNA_def_function_return(func, parm); func = RNA_def_function(srna, "add_socket", "rna_ShaderNodeScript_add_socket"); - RNA_def_function_ui_description(func, "Add a socket socket"); + RNA_def_function_ui_description(func, "Add a socket"); RNA_def_function_flag(func, FUNC_USE_SELF_ID); parm = RNA_def_string(func, "name", NULL, 0, "Name", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); @@ -6488,7 +6488,7 @@ static void def_sh_script(StructRNA *srna) RNA_def_function_return(func, parm); func = RNA_def_function(srna, "remove_socket", "rna_ShaderNodeScript_remove_socket"); - RNA_def_function_ui_description(func, "Remove a socket socket"); + RNA_def_function_ui_description(func, "Remove a socket"); RNA_def_function_flag(func, FUNC_USE_SELF_ID); parm = RNA_def_pointer(func, "sock", "NodeSocket", "Socket", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc index 5d570657b53..f14e2113e13 100644 --- a/source/blender/makesrna/intern/rna_path.cc +++ b/source/blender/makesrna/intern/rna_path.cc @@ -940,7 +940,7 @@ ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path) BLI_assert_msg(0, "Missing handling of embedded id type."); return id; } - return id_type->owner_get(bmain, id); + return id_type->owner_get(bmain, id, nullptr); } static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id) diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 30df8e20e8d..e1a46b01db2 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -600,7 +600,7 @@ static void rna_PoseChannel_constraints_remove( ED_object_constraint_update(bmain, ob); - /* XXX(Campbell): is this really needed? */ + /* XXX(@campbellbarton): is this really needed? */ BKE_constraints_active_set(&pchan->constraints, NULL); WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, id); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index f24aec3447b..2e78cc97099 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1295,7 +1295,7 @@ static const EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf(bContext ID *id = ptr->owner_id; const bool is_render = (id && GS(id->name) == ID_SCE); - /* NOTE(campbell): we need to act differently for render + /* NOTE(@campbellbarton): we need to act differently for render * where 'BW' will force grayscale even if the output format writes * as RGBA, this is age old blender convention and not sure how useful * it really is but keep it for now. */ @@ -1478,7 +1478,7 @@ static void rna_ImageFormatSettings_color_management_set(PointerRNA *ptr, int va if (owner_id && GS(owner_id->name) == ID_NT) { /* For compositing nodes, find the corresponding scene. */ const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(owner_id); - owner_id = type_info->owner_get(G_MAIN, owner_id); + owner_id = type_info->owner_get(G_MAIN, owner_id, NULL); } if (owner_id && GS(owner_id->name) == ID_SCE) { BKE_image_format_color_management_copy_from_scene(imf, (Scene *)owner_id); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 2e1fa8db7fe..4cd0b27c772 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1040,7 +1040,7 @@ static void rna_def_paint_mode(BlenderRNA *brna) RNA_def_property_pointer_funcs( prop, NULL, NULL, NULL, "rna_PaintModeSettings_canvas_image_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE); - RNA_def_property_ui_text(prop, "Texture", "Image used as as painting target"); + RNA_def_property_ui_text(prop, "Texture", "Image used as painting target"); } static void rna_def_image_paint(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 5cee2ca00a3..502b0404764 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4199,6 +4199,14 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shader AOV Name", "Name of the active Shader AOV"); RNA_def_property_flag(prop, PROP_HIDDEN); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "use_compositor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_COMPOSITOR); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_default(prop, false); + RNA_def_property_ui_text( + prop, "Compositor", "Preview the compositor output inside the viewport"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); } static void rna_def_space_view3d_overlay(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 1b416e4b6e5..fc68e8421d7 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -1808,8 +1808,7 @@ void RNA_api_ui_layout(StructRNA *srna) func = RNA_def_function( srna, "template_colormanaged_view_settings", "uiTemplateColormanagedViewSettings"); - RNA_def_function_ui_description( - func, "Item. A widget to control color managed view settings settings."); + RNA_def_function_ui_description(func, "Item. A widget to control color managed view settings."); RNA_def_function_flag(func, FUNC_USE_CONTEXT); api_ui_item_rna_common(func); # if 0 diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index cfc72791123..438bac9b458 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6338,6 +6338,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Sculpt Mode Tilt Support", "Support for pen tablet tilt events in Sculpt Mode"); + prop = RNA_def_property(srna, "use_realtime_compositor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_realtime_compositor", 1); + RNA_def_property_ui_text(prop, "Realtime Compositor", "Enable the new realtime compositor"); + prop = RNA_def_property(srna, "use_sculpt_texture_paint", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_texture_paint", 1); RNA_def_property_ui_text(prop, "Sculpt Texture Paint", "Use texture painting in Sculpt Mode"); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 2009f51e1f2..ac1803b0a11 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -864,6 +864,21 @@ static void rna_Window_view_layer_set(PointerRNA *ptr, WM_window_set_active_view_layer(win, view_layer); } +static void rna_KeyMap_modal_event_values_items_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ + wmKeyMap *km = ptr->data; + + const EnumPropertyItem *items = rna_enum_keymap_propvalue_items; + if ((km->flag & KEYMAP_MODAL) != 0 && km->modal_items != NULL) { + items = km->modal_items; + } + + const int totitem = RNA_enum_items_count(items); + + rna_iterator_array_begin(iter, (void *)items, sizeof(EnumPropertyItem), totitem, false, NULL); +} + static PointerRNA rna_KeyMapItem_properties_get(PointerRNA *ptr) { wmKeyMapItem *kmi = ptr->data; @@ -2617,6 +2632,23 @@ static void rna_def_keyconfig(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Children Expanded", "Children expanded in the user interface"); RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1); + prop = RNA_def_property(srna, "modal_event_values", PROP_COLLECTION, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_struct_type(prop, "EnumPropertyItem"); + RNA_def_property_collection_funcs(prop, + "rna_KeyMap_modal_event_values_items_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + NULL, + NULL, + NULL, + NULL); + RNA_def_property_ui_text(prop, + "Modal Events", + "Give access to the possible event values of this modal keymap's items " + "(#KeyMapItem.propvalue), for API introspection"); + RNA_api_keymap(srna); /* KeyMapItem */ diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 3fce556ce77..43f650e025c 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -211,8 +211,7 @@ static void deformMatrices(ModifierData *md, int verts_num) { ArmatureModifierData *amd = (ArmatureModifierData *)md; - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); BKE_armature_deform_coords_with_mesh(amd->object, ctx->object, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 874dd20691f..e17a612376d 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -467,7 +467,7 @@ static void deformVerts(ModifierData *md, if (ctx->object->type == OB_MESH && cmd->defgrp_name[0] != '\0') { /* mesh_src is only needed for vgroups. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } if (cmd->type == MOD_CAST_TYPE_CUBOID) { @@ -493,15 +493,14 @@ static void deformVertsEM(ModifierData *md, Mesh *mesh_src = NULL; if (cmd->defgrp_name[0] != '\0') { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { BLI_assert(mesh->totvert == verts_num); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 4fe65bf28ac..11bbe8dc83e 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -94,7 +94,7 @@ static void deformVerts(ModifierData *md, } if (mesh == NULL) { - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false); } else { /* Not possible to use get_mesh() in this case as we'll modify its vertices diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 868e164e223..42a8ba804ed 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -107,7 +107,7 @@ static void deformVerts(ModifierData *md, } if (mesh == NULL) { - mesh_src = MOD_deform_mesh_eval_get(ob, NULL, NULL, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ob, NULL, NULL, NULL, verts_num, false); } else { /* Not possible to use get_mesh() in this case as we'll modify its vertices diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 2beb1be6749..4df0479372f 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -729,8 +729,7 @@ static void deformVerts(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); correctivesmooth_modifier_do( md, ctx->depsgraph, ctx->object, mesh_src, vertexCos, (uint)verts_num, NULL); @@ -747,10 +746,9 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index bd622fc1373..af639915bd8 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -111,7 +111,7 @@ static void deformVerts(ModifierData *md, if (ctx->object->type == OB_MESH && cmd->name[0] != '\0') { /* mesh_src is only needed for vgroups. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } struct MDeformVert *dvert = NULL; diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 3df4fbcbea8..55d9d148d10 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -201,11 +201,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * updateFaceCount(ctx, dmd, bm->totface); - result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); /* make sure we never alloc'd these */ BLI_assert(bm->vtoolflagpool == NULL && bm->etoolflagpool == NULL && bm->ftoolflagpool == NULL); BLI_assert(bm->vtable == NULL && bm->etable == NULL && bm->ftable == NULL); + result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); #ifdef USE_TIMEIT diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index cf1bb64a41d..367809953b6 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -372,8 +372,7 @@ static void deformVerts(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); displaceModifier_do((DisplaceModifierData *)md, ctx, mesh_src, vertexCos, verts_num); @@ -389,10 +388,9 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index ff0616fd288..e243c32173d 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -742,7 +742,7 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh) /* override original facepa (original pointer is saved in caller function) */ - /* TODO(campbell): `(totfsplit * 2)` over allocation is used since the quads are + /* TODO(@campbellbarton): `(totfsplit * 2)` over allocation is used since the quads are * later interpreted as tri's, for this to work right I think we probably * have to stop using tessface. */ diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index b9942ff8a4d..979a08483e1 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -430,8 +430,7 @@ static void deformVerts(struct ModifierData *md, int verts_num) { HookModifierData *hmd = (HookModifierData *)md; - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); deformVerts_do(hmd, ctx, ctx->object, mesh_src, NULL, vertexCos, verts_num); diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index e29098eb218..6333eb699b3 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -764,8 +764,7 @@ static void deformVerts(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); LaplacianDeformModifier_do( (LaplacianDeformModifierData *)md, ctx->object, mesh_src, vertexCos, verts_num); @@ -782,10 +781,9 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index 2cce0c14e4c..c42f7b33919 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -535,7 +535,7 @@ static void deformVerts(ModifierData *md, return; } - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); laplaciansmoothModifier_do( (LaplacianSmoothModifierData *)md, ctx->object, mesh_src, vertexCos, verts_num); @@ -558,9 +558,9 @@ static void deformVertsEM(ModifierData *md, return; } - mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 2edcbd8e59a..81b60b660c6 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -98,7 +98,7 @@ static void deformVerts(ModifierData *md, { LatticeModifierData *lmd = (LatticeModifierData *)md; struct Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + ctx->object, NULL, mesh, NULL, verts_num, false); MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */ diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 8dfdd07ace9..3e81f987da3 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -297,7 +297,7 @@ static void deformVerts(ModifierData *md, if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') { /* `mesh_src` is only needed for vertex groups. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, verts_num); @@ -320,8 +320,7 @@ static void deformVertsEM(ModifierData *md, if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') { /* `mesh_src` is only needed for vertex groups. */ - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index 3b7e62f99e1..d1df86b1010 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -444,8 +444,7 @@ static void deformVerts(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */ @@ -463,10 +462,9 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 5018b2d1030..d6435c55211 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -553,7 +553,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) "particle_system", &particle_obj_ptr, "particle_systems", - "Particle System", + IFACE_("Particle System"), ICON_NONE); } else { diff --git a/source/blender/modifiers/intern/MOD_particlesystem.cc b/source/blender/modifiers/intern/MOD_particlesystem.cc index 7f7465947f9..0c04c6fc062 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.cc +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -119,8 +119,7 @@ static void deformVerts(ModifierData *md, } if (mesh_src == nullptr) { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, nullptr, nullptr, vertexCos, verts_num, true); if (mesh_src == nullptr) { return; } diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 109795df796..6095be48f8f 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -52,13 +52,18 @@ static void initData(ModifierData *md) #include "BLI_strict_flags.h" -/* used for gathering edge connectivity */ +/** Used for gathering edge connectivity. */ typedef struct ScrewVertConnect { - float dist; /* distance from the center axis */ - float co[3]; /* location relative to the transformed axis */ - float no[3]; /* calc normal of the vertex */ - uint v[2]; /* 2 verts on either side of this one */ - MEdge *e[2]; /* edges on either side, a bit of a waste since each edge ref's 2 edges */ + /** Distance from the center axis. */ + float dist_sq; + /** Location relative to the transformed axis. */ + float co[3]; + /** Calc normal of the vertex. */ + float no[3]; + /** 2 verts on either side of this one. */ + uint v[2]; + /** Edges on either side, a bit of a waste since each edge ref's 2 edges. */ + MEdge *e[2]; char flag; } ScrewVertConnect; @@ -270,18 +275,18 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * axis_vec[ltmd->axis] = 1.0f; if (ob_axis != NULL) { - /* calc the matrix relative to the axis object */ + /* Calculate the matrix relative to the axis object. */ invert_m4_m4(mtx_tmp_a, ctx->object->obmat); copy_m4_m4(mtx_tx_inv, ob_axis->obmat); mul_m4_m4m4(mtx_tx, mtx_tmp_a, mtx_tx_inv); - /* calc the axis vec */ + /* Calculate the axis vector. */ mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */ normalize_v3(axis_vec); /* screw */ if (ltmd->flag & MOD_SCREW_OBJECT_OFFSET) { - /* find the offset along this axis relative to this objects matrix */ + /* Find the offset along this axis relative to this objects matrix. */ float totlen = len_v3(mtx_tx[3]); if (totlen != 0.0f) { @@ -330,7 +335,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * else { axis_char = (char)(axis_char + ltmd->axis); /* 'X' + axis */ - /* useful to be able to use the axis vec in some cases still */ + /* Useful to be able to use the axis vector in some cases still. */ zero_v3(axis_vec); axis_vec[ltmd->axis] = 1.0f; } @@ -441,7 +446,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * med_new->crease = med_orig->crease; med_new->flag = med_orig->flag & ~ME_LOOSEEDGE; - /* Tag mvert as not loose. */ + /* Tag #MVert as not loose. */ BLI_BITMAP_ENABLE(vert_tag, med_orig->v1); BLI_BITMAP_ENABLE(vert_tag, med_orig->v2); } @@ -481,8 +486,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * if (ltmd->flag & MOD_SCREW_NORMAL_CALC) { - /* - * Normal Calculation (for face flipping) + /* Normal Calculation (for face flipping) * Sort edge verts for correct face flipping * NOT REALLY NEEDED but face flipping is nice. */ @@ -490,19 +494,19 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * * * Since we are only ordering the edges here it can avoid mallocing the * extra space by abusing the vert array before its filled with new verts. - * The new array for vert_connect must be at least sizeof(ScrewVertConnect) * totvert - * and the size of our resulting meshes array is sizeof(MVert) * totvert * 3 - * so its safe to use the second 2 thirds of MVert the array for vert_connect, - * just make sure ScrewVertConnect struct is no more than twice as big as MVert, + * The new array for vert_connect must be at least `sizeof(ScrewVertConnect) * totvert` + * and the size of our resulting meshes array is `sizeof(MVert) * totvert * 3` + * so its safe to use the second 2 thirds of #MVert the array for vert_connect, + * just make sure #ScrewVertConnect struct is no more than twice as big as #MVert, * at the moment there is no chance of that being a problem, - * unless MVert becomes half its current size. + * unless #MVert becomes half its current size. * * once the edges are ordered, vert_connect is not needed and it can be used for verts * - * This makes the modifier faster with one less alloc. + * This makes the modifier faster with one less allocate. */ - vert_connect = MEM_malloc_arrayN(totvert, sizeof(ScrewVertConnect), "ScrewVertConnect"); + vert_connect = MEM_malloc_arrayN(totvert, sizeof(ScrewVertConnect), __func__); /* skip the first slice of verts. */ // vert_connect = (ScrewVertConnect *) &medge_new[totvert]; vc = vert_connect; @@ -512,7 +516,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * if (!totedge) { for (i = 0; i < totvert; i++, mv_orig++, mv_new++) { copy_v3_v3(mv_new->co, mv_orig->co); - normalize_v3_v3(vc->no, mv_new->co); /* no edges- this is really a dummy normal */ + /* No edges: this is really a dummy normal. */ + normalize_v3_v3(vc->no, mv_new->co); } } else { @@ -533,11 +538,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * vc->v[0] = vc->v[1] = SV_UNUSED; mul_m4_v3(mtx_tx, vc->co); - /* length in 2d, don't sqrt because this is only for comparison */ - vc->dist = vc->co[other_axis_1] * vc->co[other_axis_1] + - vc->co[other_axis_2] * vc->co[other_axis_2]; + /* Length in 2D, don't `sqrt` because this is only for comparison. */ + vc->dist_sq = vc->co[other_axis_1] * vc->co[other_axis_1] + + vc->co[other_axis_2] * vc->co[other_axis_2]; - // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist); + // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist_sq); } } else { @@ -550,11 +555,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * vc->e[0] = vc->e[1] = NULL; vc->v[0] = vc->v[1] = SV_UNUSED; - /* length in 2d, don't sqrt because this is only for comparison */ - vc->dist = vc->co[other_axis_1] * vc->co[other_axis_1] + - vc->co[other_axis_2] * vc->co[other_axis_2]; + /* Length in 2D, don't sqrt because this is only for comparison. */ + vc->dist_sq = vc->co[other_axis_1] * vc->co[other_axis_1] + + vc->co[other_axis_2] * vc->co[other_axis_2]; - // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist); + // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist_sq); } } @@ -622,9 +627,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } lt_iter.v_poin->flag = 1; vc_tot_linked++; - // printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist); - if (fl <= lt_iter.v_poin->dist) { - fl = lt_iter.v_poin->dist; + // printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist_sq); + if (fl <= lt_iter.v_poin->dist_sq) { + fl = lt_iter.v_poin->dist_sq; v_best = lt_iter.v; // printf("\t\t\tVERT BEST: %i\n", v_best); } diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index 1c4ef5698b4..4a927d92956 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -108,7 +108,7 @@ static void deformVerts(ModifierData *md, (swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) { /* mesh_src is needed for vgroups, but also used as ShrinkwrapCalcData.vert when projecting. * Avoid time-consuming mesh conversion for curves when not projecting. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } struct MDeformVert *dvert = NULL; @@ -135,11 +135,10 @@ static void deformVertsEM(ModifierData *md, Mesh *mesh_src = NULL; if ((swmd->vgroup_name[0] != '\0') || (swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index 168575b6330..1fc4f11bc66 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -453,7 +453,7 @@ static void deformVerts(ModifierData *md, if (ctx->object->type == OB_MESH && sdmd->vgroup_name[0] != '\0') { /* mesh_src is only needed for vgroups. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } SimpleDeformModifier_do(sdmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); @@ -475,11 +475,10 @@ static void deformVertsEM(ModifierData *md, if (ctx->object->type == OB_MESH && sdmd->vgroup_name[0] != '\0') { /* mesh_src is only needed for vgroups. */ - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c index c868c47cb90..6dd3d491283 100644 --- a/source/blender/modifiers/intern/MOD_smooth.c +++ b/source/blender/modifiers/intern/MOD_smooth.c @@ -190,7 +190,7 @@ static void deformVerts(ModifierData *md, Mesh *mesh_src = NULL; /* mesh_src is needed for vgroups, and taking edges into account. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); smoothModifier_do(smd, ctx->object, mesh_src, vertexCos, verts_num); @@ -210,9 +210,9 @@ static void deformVertsEM(ModifierData *md, Mesh *mesh_src = NULL; /* mesh_src is needed for vgroups, and taking edges into account. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ BKE_mesh_wrapper_ensure_mdata(mesh_src); smoothModifier_do(smd, ctx->object, mesh_src, vertexCos, verts_num); diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 8bd66de966f..2247a6331fe 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -1016,9 +1016,9 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex if (do_rim) { uint i; - /* NOTE(campbell): Unfortunately re-calculate the normals for the new edge faces is necessary. - * This could be done in many ways, but probably the quickest way - * is to calculate the average normals for side faces only. + /* NOTE(@campbellbarton): Unfortunately re-calculate the normals for the new edge + * faces is necessary. This could be done in many ways, but probably the quickest + * way is to calculate the average normals for side faces only. * Then blend them with the normals of the edge verts. * * At the moment its easiest to allocate an entire array for every vertex, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 8cfe3b35949..3e5a577a806 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -114,7 +114,7 @@ static void deformVerts(ModifierData *md, surmd->mesh = (Mesh *)BKE_id_copy_ex(NULL, (ID *)mesh, NULL, LIB_ID_COPY_LOCALIZE); } else { - surmd->mesh = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false, false); + surmd->mesh = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false); } if (!ctx->object->pd) { diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index ad19ecf5720..3c0842a6e93 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -215,8 +215,7 @@ static void freeData(ModifierData *md) MEM_SAFE_FREE(smd->verts[i].binds[j].vert_inds); MEM_SAFE_FREE(smd->verts[i].binds[j].vert_weights); } - - MEM_SAFE_FREE(smd->verts[i].binds); + MEM_freeN(smd->verts[i].binds); } } @@ -1578,7 +1577,7 @@ static void deformVerts(ModifierData *md, if (smd->defgrp_name[0] != '\0') { /* Only need to use mesh_src when a vgroup is used. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } surfacedeformModifier_do(md, ctx, vertexCos, verts_num, ctx->object, mesh_src); @@ -1600,7 +1599,7 @@ static void deformVertsEM(ModifierData *md, if (smd->defgrp_name[0] != '\0') { /* Only need to use mesh_src when a vgroup is used. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false); } surfacedeformModifier_do(md, ctx, vertexCos, verts_num, ctx->object, mesh_src); diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 575182a846b..fc17ddffa87 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -169,7 +169,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, Mesh *mesh, const float (*vertexCos)[3], const int verts_num, - const bool use_normals, const bool use_orco) { if (mesh != NULL) { @@ -217,14 +216,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, } } - /* TODO: Remove this "use_normals" argument, since the caller should retrieve normals afterwards - * if necessary. */ - if (use_normals) { - if (LIKELY(mesh)) { - BKE_mesh_vertex_normals_ensure(mesh); - } - } - if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { BLI_assert(mesh->totvert == verts_num); } diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index b3b75898557..b675c11b370 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -42,7 +42,6 @@ struct Mesh *MOD_deform_mesh_eval_get(struct Object *ob, struct Mesh *mesh, const float (*vertexCos)[3], int verts_num, - bool use_normals, bool use_orco); void MOD_get_vgroup(struct Object *ob, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 5bef19da53a..0968d0646a5 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -348,7 +348,7 @@ static void deformVerts(ModifierData *md, if (wmd->defgrp_name[0] != '\0' || wmd->texture != NULL) { /* mesh_src is only needed for vgroups and textures. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } warpModifier_do(wmd, ctx, mesh_src, vertexCos, verts_num); @@ -370,10 +370,10 @@ static void deformVertsEM(ModifierData *md, if (wmd->defgrp_name[0] != '\0' || wmd->texture != NULL) { /* mesh_src is only needed for vgroups and textures. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 136ff6b6d15..9647f47c6e0 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -302,11 +302,10 @@ static void deformVerts(ModifierData *md, Mesh *mesh_src = NULL; if (wmd->flag & MOD_WAVE_NORM) { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, vertexCos, verts_num, true, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, vertexCos, verts_num, false); } else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') { - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); @@ -327,15 +326,13 @@ static void deformVertsEM(ModifierData *md, Mesh *mesh_src = NULL; if (wmd->flag & MOD_WAVE_NORM) { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, vertexCos, verts_num, true, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, vertexCos, verts_num, false); } else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } @@ -343,6 +340,12 @@ static void deformVertsEM(ModifierData *md, waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); if (!ELEM(mesh_src, NULL, mesh)) { + /* Important not to free `vertexCos` owned by the caller. */ + EditMeshData *edit_data = mesh_src->runtime.edit_data; + if (edit_data->vertexCos == vertexCos) { + edit_data->vertexCos = NULL; + } + BKE_id_free(NULL, mesh_src); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 7ffaa120ba2..8ccf140e665 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -316,7 +316,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) sub = uiLayoutRow(sub, true); uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_add")); uiLayoutSetPropSep(sub, false); - uiItemR(sub, ptr, "add_threshold", UI_ITEM_R_SLIDER, "Threshold", ICON_NONE); + uiItemR(sub, ptr, "add_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE); uiItemDecoratorR(row, ptr, "add_threshold", 0); col = uiLayoutColumnWithHeading(layout, false, IFACE_("Group Remove")); @@ -327,7 +327,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) sub = uiLayoutRow(sub, true); uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_remove")); uiLayoutSetPropSep(sub, false); - uiItemR(sub, ptr, "remove_threshold", UI_ITEM_R_SLIDER, "Threshold", ICON_NONE); + uiItemR(sub, ptr, "remove_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE); uiItemDecoratorR(row, ptr, "remove_threshold", 0); uiItemR(layout, ptr, "normalize", 0, NULL, ICON_NONE); diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 4e78f6c1142..d8b8c354230 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -88,6 +88,14 @@ class SocketDeclaration { InputSocketFieldType input_field_type_ = InputSocketFieldType::None; OutputFieldDependency output_field_dependency_; + /** The priority of the input for determining the domain of the node. See + * realtime_compositor::InputDescriptor for more information. */ + int compositor_domain_priority_ = 0; + + /** This input expects a single value and can't operate on non-single values. See + * realtime_compositor::InputDescriptor for more information. */ + bool compositor_expects_single_value_ = false; + /** Utility method to make the socket available if there is a straightforward way to do so. */ std::function<void(bNode &)> make_available_fn_; @@ -124,6 +132,9 @@ class SocketDeclaration { InputSocketFieldType input_field_type() const; const OutputFieldDependency &output_field_dependency() const; + int compositor_domain_priority() const; + bool compositor_expects_single_value() const; + protected: void set_common_flags(bNodeSocket &socket) const; bool matches_common_data(const bNodeSocket &socket) const; @@ -238,6 +249,22 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { return *(Self *)this; } + /** The priority of the input for determining the domain of the node. See + * realtime_compositor::InputDescriptor for more information. */ + Self &compositor_domain_priority(int priority) + { + decl_->compositor_domain_priority_ = priority; + return *(Self *)this; + } + + /** This input expects a single value and can't operate on non-single values. See + * realtime_compositor::InputDescriptor for more information. */ + Self &compositor_expects_single_value(bool value = true) + { + decl_->compositor_expects_single_value_ = value; + return *(Self *)this; + } + /** * Pass a function that sets properties on the node required to make the corresponding socket * available, if it is not available on the default state of the node. The function is allowed to @@ -428,6 +455,16 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() return output_field_dependency_; } +inline int SocketDeclaration::compositor_domain_priority() const +{ + return compositor_domain_priority_; +} + +inline bool SocketDeclaration::compositor_expects_single_value() const +{ + return compositor_expects_single_value_; +} + inline void SocketDeclaration::make_available(bNode &node) const { if (make_available_fn_) { diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index c0100d77889..2537e8e93cc 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -10,11 +10,14 @@ set(INC ../../blenlib ../../blentranslation ../../depsgraph + ../../functions + ../../gpu ../../imbuf ../../makesdna ../../makesrna ../../render ../../windowmanager + ../../compositor/realtime_compositor ../../../../intern/guardedalloc # dna_type_offsets.h @@ -120,15 +123,19 @@ set(SRC node_composite_util.hh ) +set(LIB + bf_realtime_compositor +) + if(WITH_IMAGE_OPENEXR) add_definitions(-DWITH_OPENEXR) endif() -if(WITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) list(APPEND INC ../../compositor ) - add_definitions(-DWITH_COMPOSITOR) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_OPENIMAGEDENOISE) diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index 6efe6f231f5..9792c55b590 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -32,7 +32,7 @@ #include "NOD_composite.h" #include "node_composite_util.hh" -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU # include "COM_compositor.h" #endif @@ -210,7 +210,7 @@ void ntreeCompositExecTree(Scene *scene, int do_preview, const char *view_name) { -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU COM_execute(rd, scene, ntree, rendering, view_name); #else UNUSED_VARS(scene, ntree, rd, rendering, view_name); diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc index d392b810bc1..64c59eb24e3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc +++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** ALPHAOVER ******************** */ @@ -16,9 +20,18 @@ namespace blender::nodes::node_composite_alpha_over_cc { static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(2); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Image"), "Image_001") + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -36,6 +49,52 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "premul", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class AlphaOverShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float premultiply_factor = get_premultiply_factor(); + if (premultiply_factor != 0.0f) { + GPU_stack_link(material, + &bnode(), + "node_composite_alpha_over_mixed", + inputs, + outputs, + GPU_uniform(&premultiply_factor)); + return; + } + + if (get_use_premultiply()) { + GPU_stack_link(material, &bnode(), "node_composite_alpha_over_key", inputs, outputs); + return; + } + + GPU_stack_link(material, &bnode(), "node_composite_alpha_over_premultiply", inputs, outputs); + } + + bool get_use_premultiply() + { + return bnode().custom1; + } + + float get_premultiply_factor() + { + return ((NodeTwoFloats *)bnode().storage)->x; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new AlphaOverShaderNode(node); +} + } // namespace blender::nodes::node_composite_alpha_over_cc void register_node_type_cmp_alphaover() @@ -50,6 +109,7 @@ void register_node_type_cmp_alphaover() node_type_init(&ntype, file_ns::node_alphaover_init); node_type_storage( &ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc index f45b678fc50..55fe3366526 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Anti-Aliasing (SMAA 1x) ******************** */ @@ -42,6 +44,23 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class AntiAliasingOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new AntiAliasingOperation(context, node); +} + } // namespace blender::nodes::node_composite_antialiasing_cc void register_node_type_cmp_antialiasing() @@ -58,6 +77,7 @@ void register_node_type_cmp_antialiasing() node_type_init(&ntype, file_ns::node_composit_init_antialiasing); node_type_storage( &ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc index ad4a1f701d6..66a321eb088 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** BILATERALBLUR ******************** */ @@ -42,6 +44,23 @@ static void node_composit_buts_bilateralblur(uiLayout *layout, uiItemR(col, ptr, "sigma_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BilateralBlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BilateralBlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_bilateralblur_cc void register_node_type_cmp_bilateralblur() @@ -56,6 +75,7 @@ void register_node_type_cmp_bilateralblur() node_type_init(&ntype, file_ns::node_composit_init_bilateralblur); node_type_storage( &ntype, "NodeBilateralBlurData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc index 7beffe15c8e..cb1d93fe10b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_blur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** BLUR ******************** */ @@ -71,6 +73,23 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point uiItemR(col, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_blur_cc void register_node_type_cmp_blur() @@ -86,6 +105,7 @@ void register_node_type_cmp_blur() node_type_init(&ntype, file_ns::node_composit_init_blur); node_type_storage( &ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index a936bafe671..538f00af12d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** BLUR ******************** */ @@ -37,6 +39,23 @@ static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BokehBlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BokehBlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_bokehblur_cc void register_node_type_cmp_bokehblur() @@ -49,6 +68,7 @@ void register_node_type_cmp_bokehblur() ntype.declare = file_ns::cmp_node_bokehblur_declare; ntype.draw_buttons = file_ns::node_composit_buts_bokehblur; node_type_init(&ntype, file_ns::node_composit_init_bokehblur); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index 8330c56736a..a11cba37191 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Bokeh image Tools ******************** */ @@ -45,6 +47,23 @@ static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "shift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BokehImageOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Image").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BokehImageOperation(context, node); +} + } // namespace blender::nodes::node_composite_bokehimage_cc void register_node_type_cmp_bokehimage() @@ -60,6 +79,7 @@ void register_node_type_cmp_bokehimage() node_type_init(&ntype, file_ns::node_composit_init_bokehimage); node_type_storage( &ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc index f39b69c63f2..9c7bb6432cb 100644 --- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include <cmath> + +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ @@ -48,6 +57,98 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BoxMaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUShader *shader = shader_manager().get(get_shader_name()); + GPU_shader_bind(shader); + + const Domain domain = compute_domain(); + + GPU_shader_uniform_2iv(shader, "domain_size", domain.size); + + GPU_shader_uniform_2fv(shader, "location", get_location()); + GPU_shader_uniform_2fv(shader, "size", get_size() / 2.0f); + GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle())); + GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle())); + + const Result &input_mask = get_input("Mask"); + input_mask.bind_as_texture(shader, "base_mask_tx"); + + const Result &value = get_input("Value"); + value.bind_as_texture(shader, "mask_value_tx"); + + Result &output_mask = get_result("Mask"); + output_mask.allocate_texture(domain); + output_mask.bind_as_image(shader, "output_mask_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_mask.unbind_as_texture(); + value.unbind_as_texture(); + output_mask.unbind_as_image(); + GPU_shader_unbind(); + } + + Domain compute_domain() override + { + if (get_input("Mask").is_single_value()) { + return Domain(context().get_output_size()); + } + return get_input("Mask").domain(); + } + + CMPNodeMaskType get_mask_type() + { + return (CMPNodeMaskType)bnode().custom1; + } + + const char *get_shader_name() + { + switch (get_mask_type()) { + default: + case CMP_NODE_MASKTYPE_ADD: + return "compositor_box_mask_add"; + case CMP_NODE_MASKTYPE_SUBTRACT: + return "compositor_box_mask_subtract"; + case CMP_NODE_MASKTYPE_MULTIPLY: + return "compositor_box_mask_multiply"; + case CMP_NODE_MASKTYPE_NOT: + return "compositor_box_mask_not"; + } + } + + NodeBoxMask &get_node_box_mask() + { + return *static_cast<NodeBoxMask *>(bnode().storage); + } + + float2 get_location() + { + return float2(get_node_box_mask().x, get_node_box_mask().y); + } + + float2 get_size() + { + return float2(get_node_box_mask().width, get_node_box_mask().height); + } + + float get_angle() + { + return get_node_box_mask().rotation; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BoxMaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_boxmask_cc void register_node_type_cmp_boxmask() @@ -61,6 +162,7 @@ void register_node_type_cmp_boxmask() ntype.draw_buttons = file_ns::node_composit_buts_boxmask; node_type_init(&ntype, file_ns::node_composit_init_boxmask); node_type_storage(&ntype, "NodeBoxMask", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc index 65ed2885d9b..fa22f551de6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Bright and Contrast ******************** */ @@ -16,9 +20,11 @@ namespace blender::nodes::node_composite_brightness_cc { static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f); - b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f).compositor_domain_priority(1); + b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f).compositor_domain_priority(2); b.add_output<decl::Color>(N_("Image")); } @@ -34,6 +40,38 @@ static void node_composit_buts_brightcontrast(uiLayout *layout, uiItemR(layout, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BrightContrastShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float use_premultiply = get_use_premultiply(); + + GPU_stack_link(material, + &bnode(), + "node_composite_bright_contrast", + inputs, + outputs, + GPU_constant(&use_premultiply)); + } + + bool get_use_premultiply() + { + return bnode().custom1; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new BrightContrastShaderNode(node); +} + } // namespace blender::nodes::node_composite_brightness_cc void register_node_type_cmp_brightcontrast() @@ -46,6 +84,7 @@ void register_node_type_cmp_brightcontrast() ntype.declare = file_ns::cmp_node_brightcontrast_declare; ntype.draw_buttons = file_ns::node_composit_buts_brightcontrast; node_type_init(&ntype, file_ns::node_composit_init_brightcontrast); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc index 627f07fdfce..018632f776c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc @@ -10,6 +10,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Channel Matte Node ********************************* */ @@ -18,7 +22,9 @@ namespace blender::nodes::node_composite_channel_matte_cc { static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -79,6 +85,96 @@ static void node_composit_buts_channel_matte(uiLayout *layout, col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ChannelMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float color_space = get_color_space(); + const float matte_channel = get_matte_channel(); + float limit_channels[2]; + get_limit_channels(limit_channels); + const float max_limit = get_max_limit(); + const float min_limit = get_min_limit(); + + GPU_stack_link(material, + &bnode(), + "node_composite_channel_matte", + inputs, + outputs, + GPU_constant(&color_space), + GPU_constant(&matte_channel), + GPU_constant(limit_channels), + GPU_uniform(&max_limit), + GPU_uniform(&min_limit)); + } + + /* 1 -> CMP_NODE_CHANNEL_MATTE_CS_RGB + * 2 -> CMP_NODE_CHANNEL_MATTE_CS_HSV + * 3 -> CMP_NODE_CHANNEL_MATTE_CS_YUV + * 4 -> CMP_NODE_CHANNEL_MATTE_CS_YCC */ + int get_color_space() + { + return bnode().custom1; + } + + /* Get the index of the channel used to generate the matte. */ + int get_matte_channel() + { + return bnode().custom2 - 1; + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + /* Get the index of the channel used to compute the limit value. */ + int get_limit_channel() + { + return get_node_chroma()->channel - 1; + } + + /* Get the indices of the channels used to compute the limit value. We always assume the limit + * algorithm is Max, if it is a single limit channel, store it in both limit channels, because + * the maximum of two identical values is the same value. */ + void get_limit_channels(float limit_channels[2]) + { + if (get_node_chroma()->algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) { + /* If the algorithm is Max, store the indices of the other two channels other than the matte + * channel. */ + limit_channels[0] = (get_matte_channel() + 1) % 3; + limit_channels[1] = (get_matte_channel() + 2) % 3; + } + else { + /* If the algorithm is Single, store the index of the limit channel in both channels. */ + limit_channels[0] = get_limit_channel(); + limit_channels[1] = get_limit_channel(); + } + } + + float get_max_limit() + { + return get_node_chroma()->t1; + } + + float get_min_limit() + { + return get_node_chroma()->t2; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ChannelMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_channel_matte_cc void register_node_type_cmp_channel_matte() @@ -93,6 +189,7 @@ void register_node_type_cmp_channel_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_channel_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc index 69319c6825d..cb3648c5680 100644 --- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc @@ -5,11 +5,17 @@ * \ingroup cmpnodes */ +#include <cmath> + #include "BLI_math_rotation.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Chroma Key ********************************************************** */ @@ -18,8 +24,12 @@ namespace blender::nodes::node_composite_chroma_matte_cc { static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Key Color")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -51,6 +61,57 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C // uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ChromaMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float acceptance = get_acceptance(); + const float cutoff = get_cutoff(); + const float falloff = get_falloff(); + + GPU_stack_link(material, + &bnode(), + "node_composite_chroma_matte", + inputs, + outputs, + GPU_uniform(&acceptance), + GPU_uniform(&cutoff), + GPU_uniform(&falloff)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + float get_acceptance() + { + return std::tan(get_node_chroma()->t1) / 2.0f; + } + + float get_cutoff() + { + return get_node_chroma()->t2; + } + + float get_falloff() + { + return get_node_chroma()->fstrength; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ChromaMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_chroma_matte_cc void register_node_type_cmp_chroma_matte() @@ -65,6 +126,7 @@ void register_node_type_cmp_chroma_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_chroma_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc index 474fb1b72f2..5e3aaf512e6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Color Matte ********************************************************** */ @@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_color_matte_cc { static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Key Color")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -50,6 +58,58 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C) col, ptr, "color_value", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ColorMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float hue_epsilon = get_hue_epsilon(); + const float saturation_epsilon = get_saturation_epsilon(); + const float value_epsilon = get_value_epsilon(); + + GPU_stack_link(material, + &bnode(), + "node_composite_color_matte", + inputs, + outputs, + GPU_uniform(&hue_epsilon), + GPU_uniform(&saturation_epsilon), + GPU_uniform(&value_epsilon)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + float get_hue_epsilon() + { + /* Divide by 2 because the hue wraps around. */ + return get_node_chroma()->t1 / 2.0f; + } + + float get_saturation_epsilon() + { + return get_node_chroma()->t2; + } + + float get_value_epsilon() + { + return get_node_chroma()->t3; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_color_matte_cc void register_node_type_cmp_color_matte() @@ -64,6 +124,7 @@ void register_node_type_cmp_color_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_color_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc index 9ad5dfbaeb2..9744c01a256 100644 --- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc +++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc @@ -10,6 +10,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Color Spill Suppression ********************************* */ @@ -18,8 +22,15 @@ namespace blender::nodes::node_composite_color_spill_cc { static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -27,8 +38,8 @@ static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node { NodeColorspill *ncs = MEM_cnew<NodeColorspill>(__func__); node->storage = ncs; + node->custom2 = CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE; node->custom1 = 2; /* green channel */ - node->custom2 = 0; /* simple limit algorithm */ ncs->limchan = 0; /* limit by red */ ncs->limscale = 1.0f; /* limit scaling factor */ ncs->unspill = 0; /* do not use unspill */ @@ -80,6 +91,103 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C) } } +using namespace blender::realtime_compositor; + +class ColorSpillShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float spill_channel = get_spill_channel(); + float spill_scale[3]; + get_spill_scale(spill_scale); + float limit_channels[2]; + get_limit_channels(limit_channels); + const float limit_scale = get_limit_scale(); + + GPU_stack_link(material, + &bnode(), + "node_composite_color_spill", + inputs, + outputs, + GPU_constant(&spill_channel), + GPU_uniform(spill_scale), + GPU_constant(limit_channels), + GPU_uniform(&limit_scale)); + } + + /* Get the index of the channel used for spilling. */ + int get_spill_channel() + { + return bnode().custom1 - 1; + } + + CMPNodeColorSpillLimitAlgorithm get_limit_algorithm() + { + return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2; + } + + NodeColorspill *get_node_color_spill() + { + return static_cast<NodeColorspill *>(bnode().storage); + } + + void get_spill_scale(float spill_scale[3]) + { + const NodeColorspill *node_color_spill = get_node_color_spill(); + if (node_color_spill->unspill) { + spill_scale[0] = node_color_spill->uspillr; + spill_scale[1] = node_color_spill->uspillg; + spill_scale[2] = node_color_spill->uspillb; + spill_scale[get_spill_channel()] *= -1.0f; + } + else { + spill_scale[0] = 0.0f; + spill_scale[1] = 0.0f; + spill_scale[2] = 0.0f; + spill_scale[get_spill_channel()] = -1.0f; + } + } + + /* Get the index of the channel used for limiting. */ + int get_limit_channel() + { + return get_node_color_spill()->limchan; + } + + /* Get the indices of the channels used to compute the limit value. We always assume the limit + * algorithm is Average, if it is a single limit channel, store it in both limit channels, + * because the average of two identical values is the same value. */ + void get_limit_channels(float limit_channels[2]) + { + if (get_limit_algorithm() == CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE) { + /* If the algorithm is Average, store the indices of the other two channels other than the + * spill channel. */ + limit_channels[0] = (get_spill_channel() + 1) % 3; + limit_channels[1] = (get_spill_channel() + 2) % 3; + } + else { + /* If the algorithm is Single, store the index of the limit channel in both channels. */ + limit_channels[0] = get_limit_channel(); + limit_channels[1] = get_limit_channel(); + } + } + + float get_limit_scale() + { + return get_node_color_spill()->limscale; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorSpillShaderNode(node); +} + } // namespace blender::nodes::node_composite_color_spill_cc void register_node_type_cmp_color_spill() @@ -94,6 +202,7 @@ void register_node_type_cmp_color_spill() node_type_init(&ntype, file_ns::node_composit_init_color_spill); node_type_storage( &ntype, "NodeColorspill", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index dd081c8fc12..95675169c76 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -10,6 +10,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Color Balance ********************************* */ @@ -46,8 +50,15 @@ namespace blender::nodes::node_composite_colorbalance_cc { static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -71,7 +82,7 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); - if (RNA_enum_get(ptr, "correction_method") == 0) { + if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) { split = uiLayoutSplit(layout, 0.0f, false); col = uiLayoutColumn(split, false); @@ -116,7 +127,7 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout, { uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); - if (RNA_enum_get(ptr, "correction_method") == 0) { + if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) { uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true); uiItemR(layout, ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); @@ -139,6 +150,58 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout, } } +using namespace blender::realtime_compositor; + +class ColorBalanceShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const NodeColorBalance *node_color_balance = get_node_color_balance(); + + if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) { + GPU_stack_link(material, + &bnode(), + "node_composite_color_balance_lgg", + inputs, + outputs, + GPU_uniform(node_color_balance->lift), + GPU_uniform(node_color_balance->gamma), + GPU_uniform(node_color_balance->gain)); + return; + } + + GPU_stack_link(material, + &bnode(), + "node_composite_color_balance_asc_cdl", + inputs, + outputs, + GPU_uniform(node_color_balance->offset), + GPU_uniform(node_color_balance->power), + GPU_uniform(node_color_balance->slope), + GPU_uniform(&node_color_balance->offset_basis)); + } + + CMPNodeColorBalanceMethod get_color_balance_method() + { + return (CMPNodeColorBalanceMethod)bnode().custom1; + } + + NodeColorBalance *get_node_color_balance() + { + return static_cast<NodeColorBalance *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorBalanceShaderNode(node); +} + } // namespace blender::nodes::node_composite_colorbalance_cc void register_node_type_cmp_colorbalance() @@ -155,6 +218,7 @@ void register_node_type_cmp_colorbalance() node_type_init(&ntype, file_ns::node_composit_init_colorbalance); node_type_storage( &ntype, "NodeColorBalance", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 39ecd277cec..36e6672ce1c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -5,9 +5,15 @@ * \ingroup cmpnodes */ +#include "IMB_colormanagement.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Color Correction ********************************* */ @@ -16,8 +22,14 @@ namespace blender::nodes::node_composite_colorcorrection_cc { static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Mask")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -266,6 +278,73 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout, uiItemR(row, ptr, "midtones_end", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ColorCorrectionShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + float enabled_channels[3]; + get_enabled_channels(enabled_channels); + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + + const NodeColorCorrection *node_color_correction = get_node_color_correction(); + + GPU_stack_link(material, + &bnode(), + "node_composite_color_correction", + inputs, + outputs, + GPU_constant(enabled_channels), + GPU_uniform(&node_color_correction->startmidtones), + GPU_uniform(&node_color_correction->endmidtones), + GPU_uniform(&node_color_correction->master.saturation), + GPU_uniform(&node_color_correction->master.contrast), + GPU_uniform(&node_color_correction->master.gamma), + GPU_uniform(&node_color_correction->master.gain), + GPU_uniform(&node_color_correction->master.lift), + GPU_uniform(&node_color_correction->shadows.saturation), + GPU_uniform(&node_color_correction->shadows.contrast), + GPU_uniform(&node_color_correction->shadows.gamma), + GPU_uniform(&node_color_correction->shadows.gain), + GPU_uniform(&node_color_correction->shadows.lift), + GPU_uniform(&node_color_correction->midtones.saturation), + GPU_uniform(&node_color_correction->midtones.contrast), + GPU_uniform(&node_color_correction->midtones.gamma), + GPU_uniform(&node_color_correction->midtones.gain), + GPU_uniform(&node_color_correction->midtones.lift), + GPU_uniform(&node_color_correction->highlights.saturation), + GPU_uniform(&node_color_correction->highlights.contrast), + GPU_uniform(&node_color_correction->highlights.gamma), + GPU_uniform(&node_color_correction->highlights.gain), + GPU_uniform(&node_color_correction->highlights.lift), + GPU_constant(luminance_coefficients)); + } + + void get_enabled_channels(float enabled_channels[3]) + { + for (int i = 0; i < 3; i++) { + enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f; + } + } + + NodeColorCorrection *get_node_color_correction() + { + return static_cast<NodeColorCorrection *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorCorrectionShaderNode(node); +} + } // namespace blender::nodes::node_composite_colorcorrection_cc void register_node_type_cmp_colorcorrection() @@ -282,6 +361,7 @@ void register_node_type_cmp_colorcorrection() node_type_init(&ntype, file_ns::node_composit_init_colorcorrection); node_type_storage( &ntype, "NodeColorCorrection", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index d35ce7dc11a..68061bb434d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** COMPOSITE ******************** */ @@ -26,6 +35,125 @@ static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class CompositeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + if (image.is_single_value() && alpha.is_single_value()) { + execute_clear(); + } + else if (ignore_alpha()) { + execute_ignore_alpha(); + } + else if (!node().input_by_identifier("Alpha")->is_logically_linked()) { + execute_copy(); + } + else { + execute_set_alpha(); + } + } + + /* Executes when all inputs are single values, in which case, the output texture can just be + * cleared to the appropriate color. */ + void execute_clear() + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + float4 color = image.get_color_value(); + if (ignore_alpha()) { + color.w = 1.0f; + } + else if (node().input_by_identifier("Alpha")->is_logically_linked()) { + color.w = alpha.get_float_value(); + } + + GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color); + } + + /* Executes when the alpha channel of the image is ignored. */ + void execute_ignore_alpha() + { + GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "input_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* Executes when the image texture is written with no adjustments and can thus be copied directly + * to the output texture. */ + void execute_copy() + { + const Result &image = get_input("Image"); + + /* Make sure any prior writes to the texture are reflected before copying it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + GPU_texture_copy(context().get_output_texture(), image.texture()); + } + + /* Executes when the alpha channel of the image is set as the value of the input alpha. */ + void execute_set_alpha() + { + GPUShader *shader = shader_manager().get("compositor_set_alpha"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "image_tx"); + + const Result &alpha = get_input("Alpha"); + alpha.bind_as_texture(shader, "alpha_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + alpha.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the + * alpha channel of the image is retained, but only if the alpha input is not linked. If the + * alpha input is linked, it the value of that input will be used as the alpha of the image. */ + bool ignore_alpha() + { + return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA; + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CompositeOperation(context, node); +} + } // namespace blender::nodes::node_composite_composite_cc void register_node_type_cmp_composite() @@ -37,6 +165,7 @@ void register_node_type_cmp_composite() cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT); ntype.declare = file_ns::cmp_node_composite_declare; ntype.draw_buttons = file_ns::node_composit_buts_composite; + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.flag |= NODE_PREVIEW; ntype.no_muting = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc index 303248c3852..e36da39cca1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc +++ b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc @@ -14,6 +14,8 @@ #include "IMB_colormanagement.h" +#include "COM_node_operation.hh" + namespace blender::nodes::node_composite_convert_color_space_cc { static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b) @@ -47,6 +49,23 @@ static void node_composit_buts_convert_colorspace(uiLayout *layout, uiItemR(layout, ptr, "to_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ConvertColorSpaceOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ConvertColorSpaceOperation(context, node); +} + } // namespace blender::nodes::node_composite_convert_color_space_cc void register_node_type_cmp_convert_color_space(void) @@ -62,6 +81,7 @@ void register_node_type_cmp_convert_color_space(void) node_type_init(&ntype, file_ns::node_composit_init_convert_colorspace); node_type_storage( &ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc index 07da0da0be1..9679701a7cf 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_cornerpin_cc { @@ -32,6 +34,24 @@ static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Plane")); } +using namespace blender::realtime_compositor; + +class CornerPinOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Plane").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CornerPinOperation(context, node); +} + } // namespace blender::nodes::node_composite_cornerpin_cc void register_node_type_cmp_cornerpin() @@ -42,6 +62,7 @@ void register_node_type_cmp_cornerpin() cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_cornerpin_declare; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc index 823e1052dd0..d7331732fc7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_crop.cc +++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc @@ -5,11 +5,22 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" + +#include "DNA_node_types.h" + #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** Crop ******************** */ @@ -18,7 +29,9 @@ namespace blender::nodes::node_composite_crop_cc { static void cmp_node_crop_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -54,6 +67,161 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point } } +using namespace blender::realtime_compositor; + +class CropOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + /* The operation does nothing, so just pass the input through. */ + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + if (get_is_image_crop()) { + execute_image_crop(); + } + else { + execute_alpha_crop(); + } + } + + /* Crop by replacing areas outside of the cropping bounds with zero alpha. The output have the + * same domain as the input image. */ + void execute_alpha_crop() + { + GPUShader *shader = shader_manager().get("compositor_alpha_crop"); + GPU_shader_bind(shader); + + int2 lower_bound, upper_bound; + compute_cropping_bounds(lower_bound, upper_bound); + GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound); + GPU_shader_uniform_2iv(shader, "upper_bound", upper_bound); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + /* Crop the image into a new size that matches the cropping bounds. */ + void execute_image_crop() + { + int2 lower_bound, upper_bound; + compute_cropping_bounds(lower_bound, upper_bound); + + /* The image is cropped into nothing, so just return a single zero value. */ + if (lower_bound.x == upper_bound.x || lower_bound.y == upper_bound.y) { + Result &result = get_result("Image"); + result.allocate_invalid(); + return; + } + + GPUShader *shader = shader_manager().get("compositor_image_crop"); + GPU_shader_bind(shader); + + GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const int2 size = upper_bound - lower_bound; + + Result &output_image = get_result("Image"); + output_image.allocate_texture(Domain(size, compute_domain().transformation)); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + /* If true, the image should actually be cropped into a new size. Otherwise, if false, the region + * outside of the cropping bounds will be set to a zero alpha value. */ + bool get_is_image_crop() + { + return bnode().custom1; + } + + bool get_is_relative() + { + return bnode().custom2; + } + + NodeTwoXYs &get_node_two_xys() + { + return *static_cast<NodeTwoXYs *>(bnode().storage); + } + + /* Returns true if the operation does nothing and the input can be passed through. */ + bool is_identity() + { + const Result &input = get_input("Image"); + /* Single value inputs can't be cropped and are returned as is. */ + if (input.is_single_value()) { + return true; + } + + int2 lower_bound, upper_bound; + compute_cropping_bounds(lower_bound, upper_bound); + const int2 input_size = input.domain().size; + /* The cropping bounds cover the whole image, so no cropping happens. */ + if (lower_bound == int2(0) && upper_bound == input_size) { + return true; + } + + return false; + } + + void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound) + { + const NodeTwoXYs &node_two_xys = get_node_two_xys(); + const int2 input_size = get_input("Image").domain().size; + + if (get_is_relative()) { + /* The cropping bounds are relative to the image size. The factors are in the [0, 1] range, + * so it is guaranteed that they won't go over the input image size. */ + lower_bound.x = input_size.x * node_two_xys.fac_x1; + lower_bound.y = input_size.y * node_two_xys.fac_y2; + upper_bound.x = input_size.x * node_two_xys.fac_x2; + upper_bound.y = input_size.y * node_two_xys.fac_y1; + } + else { + /* Make sure the bounds don't go over the input image size. */ + lower_bound.x = min_ii(node_two_xys.x1, input_size.x); + lower_bound.y = min_ii(node_two_xys.y2, input_size.y); + upper_bound.x = min_ii(node_two_xys.x2, input_size.x); + upper_bound.y = min_ii(node_two_xys.y1, input_size.y); + } + + /* Make sure upper bound is actually higher than the lower bound. */ + lower_bound.x = min_ii(lower_bound.x, upper_bound.x); + lower_bound.y = min_ii(lower_bound.y, upper_bound.y); + upper_bound.x = max_ii(lower_bound.x, upper_bound.x); + upper_bound.y = max_ii(lower_bound.y, upper_bound.y); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CropOperation(context, node); +} + } // namespace blender::nodes::node_composite_crop_cc void register_node_type_cmp_crop() @@ -67,6 +235,7 @@ void register_node_type_cmp_crop() ntype.draw_buttons = file_ns::node_composit_buts_crop; node_type_init(&ntype, file_ns::node_composit_init_crop); node_type_storage(&ntype, "NodeTwoXYs", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 2d362a39814..7e5544381a4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -26,6 +26,8 @@ #include "RE_pipeline.h" +#include "COM_node_operation.hh" + #include <optional> /* -------------------------------------------------------------------- */ @@ -105,7 +107,6 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no return session; } -extern "C" { static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encoded_hash) { LISTBASE_FOREACH (CryptomatteEntry *, entry, &n.entries) { @@ -299,6 +300,25 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), return false; } +using namespace blender::realtime_compositor; + +class CryptoMatteOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Matte").allocate_invalid(); + get_result("Pick").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CryptoMatteOperation(context, node); +} + } // namespace blender::nodes::node_composite_cryptomatte_cc void register_node_type_cmp_cryptomatte() @@ -316,6 +336,8 @@ void register_node_type_cmp_cryptomatte() ntype.poll = file_ns::node_poll_cryptomatte; node_type_storage( &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); + ntype.get_compositor_operation = file_ns::get_compositor_operation; + nodeRegisterType(&ntype); } @@ -350,7 +372,7 @@ int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node) return 1; } -namespace blender::nodes::node_composite_cryptomatte_cc { +namespace blender::nodes::node_composite_legacy_cryptomatte_cc { static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node) { @@ -365,24 +387,43 @@ static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node) ntreeCompositCryptomatteAddSocket(ntree, node); } -} // namespace blender::nodes::node_composite_cryptomatte_cc +using namespace blender::realtime_compositor; + +class CryptoMatteOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("image").pass_through(get_result("Image")); + get_result("Matte").allocate_invalid(); + get_result("Pick").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CryptoMatteOperation(context, node); +} + +} // namespace blender::nodes::node_composite_legacy_cryptomatte_cc void register_node_type_cmp_cryptomatte_legacy() { - namespace legacy_file_ns = blender::nodes::node_composite_cryptomatte_cc; + namespace legacy_file_ns = blender::nodes::node_composite_legacy_cryptomatte_cc; namespace file_ns = blender::nodes::node_composite_cryptomatte_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE_LEGACY, "Cryptomatte", NODE_CLASS_MATTE); node_type_socket_templates(&ntype, nullptr, file_ns::cmp_node_cryptomatte_out); - node_type_init(&ntype, file_ns::node_init_cryptomatte_legacy); + node_type_init(&ntype, legacy_file_ns::node_init_cryptomatte_legacy); node_type_storage( &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation; nodeRegisterType(&ntype); } /** \} */ -} diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc index 802664d7934..c5d303c576a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -5,16 +5,23 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.h" + #include "BKE_colortools.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_node_operation.hh" +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** CURVE Time ******************** */ -namespace blender::nodes::node_composite_curves_cc { +namespace blender::nodes::node_composite_time_curves_cc { static void cmp_node_time_declare(NodeDeclarationBuilder &b) { @@ -29,11 +36,65 @@ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); } -} // namespace blender::nodes::node_composite_curves_cc +using namespace blender::realtime_compositor; + +class TimeCurveOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &result = get_result("Fac"); + result.allocate_single_value(); + + CurveMapping *curve_mapping = get_curve_mapping(); + BKE_curvemapping_init(curve_mapping); + const float time = BKE_curvemapping_evaluateF(curve_mapping, 0, compute_normalized_time()); + result.set_float_value(clamp_f(time, 0.0f, 1.0f)); + } + + CurveMapping *get_curve_mapping() + { + return static_cast<CurveMapping *>(bnode().storage); + } + + int get_start_time() + { + return bnode().custom1; + } + + int get_end_time() + { + return bnode().custom2; + } + + float compute_normalized_time() + { + const int frame_number = context().get_frame_number(); + if (frame_number < get_start_time()) { + return 0.0f; + } + if (frame_number > get_end_time()) { + return 1.0f; + } + if (get_start_time() == get_end_time()) { + return 0.0f; + } + return static_cast<float>(frame_number - get_start_time()) / + static_cast<float>(get_end_time() - get_start_time()); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TimeCurveOperation(context, node); +} + +} // namespace blender::nodes::node_composite_time_curves_cc void register_node_type_cmp_curve_time() { - namespace file_ns = blender::nodes::node_composite_curves_cc; + namespace file_ns = blender::nodes::node_composite_time_curves_cc; static bNodeType ntype; @@ -42,17 +103,22 @@ void register_node_type_cmp_curve_time() node_type_size(&ntype, 200, 140, 320); node_type_init(&ntype, file_ns::node_composit_init_curves_time); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } /* **************** CURVE VEC ******************** */ -namespace blender::nodes::node_composite_curves_cc { +namespace blender::nodes::node_composite_vector_curves_cc { static void cmp_node_curve_vec_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>(N_("Vector")).default_value({0.0f, 0.0f, 0.0f}).min(-1.0f).max(1.0f); + b.add_input<decl::Vector>(N_("Vector")) + .default_value({0.0f, 0.0f, 0.0f}) + .min(-1.0f) + .max(1.0f) + .compositor_domain_priority(0); b.add_output<decl::Vector>(N_("Vector")); } @@ -66,11 +132,63 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false); } -} // namespace blender::nodes::node_composite_curves_cc +using namespace blender::realtime_compositor; + +class VectorCurvesShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + CurveMapping *curve_mapping = get_curve_mapping(); + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + + GPU_stack_link(material, + &bnode(), + "curves_vector", + inputs, + outputs, + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); + } + + CurveMapping *get_curve_mapping() + { + return static_cast<CurveMapping *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new VectorCurvesShaderNode(node); +} + +} // namespace blender::nodes::node_composite_vector_curves_cc void register_node_type_cmp_curve_vec() { - namespace file_ns = blender::nodes::node_composite_curves_cc; + namespace file_ns = blender::nodes::node_composite_vector_curves_cc; static bNodeType ntype; @@ -80,19 +198,26 @@ void register_node_type_cmp_curve_vec() node_type_size(&ntype, 200, 140, 320); node_type_init(&ntype, file_ns::node_composit_init_curve_vec); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** CURVE RGB ******************** */ -namespace blender::nodes::node_composite_curves_cc { +namespace blender::nodes::node_composite_rgb_curves_cc { static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype( - PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_input<decl::Color>(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); b.add_input<decl::Color>(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_output<decl::Color>(N_("Image")); @@ -103,11 +228,105 @@ static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node) node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); } -} // namespace blender::nodes::node_composite_curves_cc +using namespace blender::realtime_compositor; + +class RGBCurvesShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + CurveMapping *curve_mapping = get_curve_mapping(); + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + + if (curve_mapping->tone == CURVE_TONE_FILMLIKE) { + GPU_stack_link(material, + &bnode(), + "curves_film_like", + inputs, + outputs, + band_texture, + GPU_constant(&band_layer), + GPU_uniform(&range_minimums[3]), + GPU_uniform(&range_dividers[3]), + GPU_uniform(&start_slopes[3]), + GPU_uniform(&end_slopes[3])); + return; + } + + const float min = 0.0f; + const float max = 1.0f; + GPU_link(material, + "clamp_value", + get_input_link("Fac"), + GPU_constant(&min), + GPU_constant(&max), + &get_input("Fac").link); + + /* If the RGB curves do nothing, use a function that skips RGB computations. */ + if (BKE_curvemapping_is_map_identity(curve_mapping, 0) && + BKE_curvemapping_is_map_identity(curve_mapping, 1) && + BKE_curvemapping_is_map_identity(curve_mapping, 2)) { + GPU_stack_link(material, + &bnode(), + "curves_combined_only", + inputs, + outputs, + band_texture, + GPU_constant(&band_layer), + GPU_uniform(&range_minimums[3]), + GPU_uniform(&range_dividers[3]), + GPU_uniform(&start_slopes[3]), + GPU_uniform(&end_slopes[3])); + return; + } + + GPU_stack_link(material, + &bnode(), + "curves_combined_rgb", + inputs, + outputs, + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); + } + + CurveMapping *get_curve_mapping() + { + return static_cast<CurveMapping *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new RGBCurvesShaderNode(node); +} + +} // namespace blender::nodes::node_composite_rgb_curves_cc void register_node_type_cmp_curve_rgb() { - namespace file_ns = blender::nodes::node_composite_curves_cc; + namespace file_ns = blender::nodes::node_composite_rgb_curves_cc; static bNodeType ntype; @@ -116,6 +335,7 @@ void register_node_type_cmp_curve_rgb() node_type_size(&ntype, 200, 140, 320); node_type_init(&ntype, file_ns::node_composit_init_curve_rgb); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.cc b/source/blender/nodes/composite/nodes/node_composite_defocus.cc index 83dd397ff1f..94b4908a1bd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_defocus.cc +++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc @@ -12,6 +12,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* ************ Defocus Node ****************** */ @@ -81,6 +83,23 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA uiItemR(sub, ptr, "z_scale", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DefocusOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DefocusOperation(context, node); +} + } // namespace blender::nodes::node_composite_defocus_cc void register_node_type_cmp_defocus() @@ -94,6 +113,7 @@ void register_node_type_cmp_defocus() ntype.draw_buttons = file_ns::node_composit_buts_defocus; node_type_init(&ntype, file_ns::node_composit_init_defocus); node_type_storage(&ntype, "NodeDefocus", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.cc b/source/blender/nodes/composite/nodes/node_composite_denoise.cc index 051a2580ef9..0452e7cd943 100644 --- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc +++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_denoise_cc { @@ -52,6 +54,23 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "use_hdr", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DenoiseOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DenoiseOperation(context, node); +} + } // namespace blender::nodes::node_composite_denoise_cc void register_node_type_cmp_denoise() @@ -65,6 +84,7 @@ void register_node_type_cmp_denoise() ntype.draw_buttons = file_ns::node_composit_buts_denoise; node_type_init(&ntype, file_ns::node_composit_init_denonise); node_type_storage(&ntype, "NodeDenoise", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc index 66a18cfa369..0b9f9c8f76d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc +++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** FILTER ******************** */ @@ -36,6 +38,23 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "threshold_neighbor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DespeckleOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DespeckleOperation(context, node); +} + } // namespace blender::nodes::node_composite_despeckle_cc void register_node_type_cmp_despeckle() @@ -49,6 +68,7 @@ void register_node_type_cmp_despeckle() ntype.draw_buttons = file_ns::node_composit_buts_despeckle; ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_despeckle); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc index b87bbe439db..e129dcaa6ef 100644 --- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* channel Difference Matte ********************************* */ @@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_diff_matte_cc { static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image 1")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Image 2")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -40,6 +48,50 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DifferenceMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float tolerance = get_tolerance(); + const float falloff = get_falloff(); + + GPU_stack_link(material, + &bnode(), + "node_composite_difference_matte", + inputs, + outputs, + GPU_uniform(&tolerance), + GPU_uniform(&falloff)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + float get_tolerance() + { + return get_node_chroma()->t1; + } + + float get_falloff() + { + return get_node_chroma()->t2; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new DifferenceMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_diff_matte_cc void register_node_type_cmp_diff_matte() @@ -54,6 +106,7 @@ void register_node_type_cmp_diff_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_diff_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc index 9bdb9ae0837..46199d3ff04 100644 --- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Dilate/Erode ******************** */ @@ -43,6 +45,23 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C) } } +using namespace blender::realtime_compositor; + +class DilateErodeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Mask").pass_through(get_result("Mask")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DilateErodeOperation(context, node); +} + } // namespace blender::nodes::node_composite_dilate_cc void register_node_type_cmp_dilateerode() @@ -57,6 +76,7 @@ void register_node_type_cmp_dilateerode() node_type_init(&ntype, file_ns::node_composit_init_dilateerode); node_type_storage( &ntype, "NodeDilateErode", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc index 3d82ab04fc9..eacba5ad12d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_directionalblur_cc { @@ -51,6 +53,23 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin uiItemR(layout, ptr, "zoom", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DirectionalBlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DirectionalBlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_directionalblur_cc void register_node_type_cmp_dblur() @@ -65,6 +84,7 @@ void register_node_type_cmp_dblur() node_type_init(&ntype, file_ns::node_composit_init_dblur); node_type_storage( &ntype, "NodeDBlurData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc index 0b0d42cbb08..1049f2fa4a9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_displace.cc +++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Displace ******************** */ @@ -24,6 +26,23 @@ static void cmp_node_displace_declare(NodeDeclarationBuilder &b) b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class DisplaceOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DisplaceOperation(context, node); +} + } // namespace blender::nodes::node_composite_displace_cc void register_node_type_cmp_displace() @@ -34,6 +53,7 @@ void register_node_type_cmp_displace() cmp_node_type_base(&ntype, CMP_NODE_DISPLACE, "Displace", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_displace_declare; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc index a8646d8498e..9d910b3f409 100644 --- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* channel Distance Matte ********************************* */ @@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_distance_matte_cc { static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Key Color")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -26,7 +34,7 @@ static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *n { NodeChroma *c = MEM_cnew<NodeChroma>(__func__); node->storage = c; - c->channel = 1; + c->channel = CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA; c->t1 = 0.1f; c->t2 = 0.1f; } @@ -48,6 +56,66 @@ static void node_composit_buts_distance_matte(uiLayout *layout, uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DistanceMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float tolerance = get_tolerance(); + const float falloff = get_falloff(); + + if (get_color_space() == CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA) { + GPU_stack_link(material, + &bnode(), + "node_composite_distance_matte_rgba", + inputs, + outputs, + GPU_uniform(&tolerance), + GPU_uniform(&falloff)); + return; + } + + GPU_stack_link(material, + &bnode(), + "node_composite_distance_matte_ycca", + inputs, + outputs, + GPU_uniform(&tolerance), + GPU_uniform(&falloff)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + CMPNodeDistanceMatteColorSpace get_color_space() + { + return (CMPNodeDistanceMatteColorSpace)get_node_chroma()->channel; + } + + float get_tolerance() + { + return get_node_chroma()->t1; + } + + float get_falloff() + { + return get_node_chroma()->t2; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new DistanceMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_distance_matte_cc void register_node_type_cmp_distance_matte() @@ -62,6 +130,7 @@ void register_node_type_cmp_distance_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_distance_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc index 9dc2b223618..fec7879ed78 100644 --- a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Double Edge Mask ******************** */ @@ -35,6 +37,23 @@ static void node_composit_buts_double_edge_mask(uiLayout *layout, uiItemR(col, ptr, "edge_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class DoubleEdgeMaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Inner Mask").pass_through(get_result("Mask")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DoubleEdgeMaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_double_edge_mask_cc void register_node_type_cmp_doubleedgemask() @@ -46,6 +65,7 @@ void register_node_type_cmp_doubleedgemask() cmp_node_type_base(&ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_MATTE); ntype.declare = file_ns::cmp_node_double_edge_mask_declare; ntype.draw_buttons = file_ns::node_composit_buts_double_edge_mask; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc index 4da6a0a442e..54dfa00eadd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include <cmath> + +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ @@ -46,6 +55,98 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C) uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class EllipseMaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUShader *shader = shader_manager().get(get_shader_name()); + GPU_shader_bind(shader); + + const Domain domain = compute_domain(); + + GPU_shader_uniform_2iv(shader, "domain_size", domain.size); + + GPU_shader_uniform_2fv(shader, "location", get_location()); + GPU_shader_uniform_2fv(shader, "radius", get_size() / 2.0f); + GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle())); + GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle())); + + const Result &input_mask = get_input("Mask"); + input_mask.bind_as_texture(shader, "base_mask_tx"); + + const Result &value = get_input("Value"); + value.bind_as_texture(shader, "mask_value_tx"); + + Result &output_mask = get_result("Mask"); + output_mask.allocate_texture(domain); + output_mask.bind_as_image(shader, "output_mask_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_mask.unbind_as_texture(); + value.unbind_as_texture(); + output_mask.unbind_as_image(); + GPU_shader_unbind(); + } + + Domain compute_domain() override + { + if (get_input("Mask").is_single_value()) { + return Domain(context().get_output_size()); + } + return get_input("Mask").domain(); + } + + CMPNodeMaskType get_mask_type() + { + return (CMPNodeMaskType)bnode().custom1; + } + + const char *get_shader_name() + { + switch (get_mask_type()) { + default: + case CMP_NODE_MASKTYPE_ADD: + return "compositor_ellipse_mask_add"; + case CMP_NODE_MASKTYPE_SUBTRACT: + return "compositor_ellipse_mask_subtract"; + case CMP_NODE_MASKTYPE_MULTIPLY: + return "compositor_ellipse_mask_multiply"; + case CMP_NODE_MASKTYPE_NOT: + return "compositor_ellipse_mask_not"; + } + } + + NodeEllipseMask &get_node_ellipse_mask() + { + return *static_cast<NodeEllipseMask *>(bnode().storage); + } + + float2 get_location() + { + return float2(get_node_ellipse_mask().x, get_node_ellipse_mask().y); + } + + float2 get_size() + { + return float2(get_node_ellipse_mask().width, get_node_ellipse_mask().height); + } + + float get_angle() + { + return get_node_ellipse_mask().rotation; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new EllipseMaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_ellipsemask_cc void register_node_type_cmp_ellipsemask() @@ -61,6 +162,7 @@ void register_node_type_cmp_ellipsemask() node_type_init(&ntype, file_ns::node_composit_init_ellipsemask); node_type_storage( &ntype, "NodeEllipseMask", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc index 881cfc11058..19b93680ff6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc +++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Exposure ******************** */ @@ -13,11 +17,33 @@ namespace blender::nodes::node_composite_exposure_cc { static void cmp_node_exposure_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f).compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class ExposureShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_exposure", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ExposureShaderNode(node); +} + } // namespace blender::nodes::node_composite_exposure_cc void register_node_type_cmp_exposure() @@ -28,6 +54,7 @@ void register_node_type_cmp_exposure() cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_exposure_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc index c343c21feb2..854cf684806 100644 --- a/source/blender/nodes/composite/nodes/node_composite_filter.cc +++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** FILTER ******************** */ @@ -26,6 +28,23 @@ static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class FilterOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new FilterOperation(context, node); +} + } // namespace blender::nodes::node_composite_filter_cc void register_node_type_cmp_filter() @@ -39,6 +58,7 @@ void register_node_type_cmp_filter() ntype.draw_buttons = file_ns::node_composit_buts_filter; ntype.labelfunc = node_filter_label; ntype.flag |= NODE_PREVIEW; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.cc b/source/blender/nodes/composite/nodes/node_composite_flip.cc index 37b9a2d020d..aaa2b565ed2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_flip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" +#include "BLI_utildefines.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** Flip ******************** */ @@ -16,7 +25,9 @@ namespace blender::nodes::node_composite_flip_cc { static void cmp_node_flip_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -25,6 +36,56 @@ static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), Point uiItemR(layout, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class FlipOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input("Image"); + Result &result = get_result("Image"); + + /* Can't flip a single value, pass it through to the output. */ + if (input.is_single_value()) { + input.pass_through(result); + return; + } + + GPUShader *shader = shader_manager().get("compositor_flip"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1b( + shader, "flip_x", ELEM(get_flip_mode(), CMP_NODE_FLIP_X, CMP_NODE_FLIP_X_Y)); + GPU_shader_uniform_1b( + shader, "flip_y", ELEM(get_flip_mode(), CMP_NODE_FLIP_Y, CMP_NODE_FLIP_X_Y)); + + input.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + + result.allocate_texture(domain); + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input.unbind_as_texture(); + result.unbind_as_image(); + GPU_shader_unbind(); + } + + CMPNodeFlipMode get_flip_mode() + { + return (CMPNodeFlipMode)bnode().custom1; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new FlipOperation(context, node); +} + } // namespace blender::nodes::node_composite_flip_cc void register_node_type_cmp_flip() @@ -36,6 +97,7 @@ void register_node_type_cmp_flip() cmp_node_type_base(&ntype, CMP_NODE_FLIP, "Flip", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_flip_declare; ntype.draw_buttons = file_ns::node_composit_buts_flip; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc index b4b8502e915..660d8068231 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Gamma Tools ******************** */ @@ -13,15 +17,38 @@ namespace blender::nodes::node_composite_gamma_cc { static void cmp_node_gamma_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_input<decl::Float>(N_("Gamma")) .default_value(1.0f) .min(0.001f) .max(10.0f) - .subtype(PROP_UNSIGNED); + .subtype(PROP_UNSIGNED) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class GammaShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_gamma", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new GammaShaderNode(node); +} + } // namespace blender::nodes::node_composite_gamma_cc void register_node_type_cmp_gamma() @@ -32,6 +59,7 @@ void register_node_type_cmp_gamma() cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_gamma_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.cc b/source/blender/nodes/composite/nodes/node_composite_glare.cc index 7f21d30cfa6..33577d5caf8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_glare.cc +++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_glare_cc { @@ -75,6 +77,23 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin } } +using namespace blender::realtime_compositor; + +class GlareOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new GlareOperation(context, node); +} + } // namespace blender::nodes::node_composite_glare_cc void register_node_type_cmp_glare() @@ -88,6 +107,7 @@ void register_node_type_cmp_glare() ntype.draw_buttons = file_ns::node_composit_buts_glare; node_type_init(&ntype, file_ns::node_composit_init_glare); node_type_storage(&ntype, "NodeGlare", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc index 08a048829df..091864a06f7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc +++ b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Hue Saturation ******************** */ @@ -13,22 +17,56 @@ namespace blender::nodes::node_composite_hue_sat_val_cc { static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Hue")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); b.add_input<decl::Float>(N_("Saturation")) .default_value(1.0f) .min(0.0f) .max(2.0f) - .subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(2); b.add_input<decl::Float>(N_("Value")) .default_value(1.0f) .min(0.0f) .max(2.0f) - .subtype(PROP_FACTOR); - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(3); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(4); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class HueSaturationValueShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_hue_saturation_value", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new HueSaturationValueShaderNode(node); +} + } // namespace blender::nodes::node_composite_hue_sat_val_cc void register_node_type_cmp_hue_sat() @@ -39,6 +77,7 @@ void register_node_type_cmp_hue_sat() cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_huesatval_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc index d252d96f8c3..a84420231aa 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -5,6 +5,12 @@ * \ingroup cmpnodes */ +#include "BKE_colortools.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" #include "BKE_colortools.h" @@ -13,8 +19,15 @@ namespace blender::nodes::node_composite_huecorrect_cc { static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -35,6 +48,53 @@ static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node) cumapping->cur = 1; } +using namespace blender::realtime_compositor; + +class HueCorrectShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + CurveMapping *curve_mapping = get_curve_mapping(); + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer); + + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + + GPU_stack_link(material, + &bnode(), + "node_composite_hue_correct", + inputs, + outputs, + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers)); + } + + CurveMapping *get_curve_mapping() + { + return static_cast<CurveMapping *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new HueCorrectShaderNode(node); +} + } // namespace blender::nodes::node_composite_huecorrect_cc void register_node_type_cmp_huecorrect() @@ -48,6 +108,7 @@ void register_node_type_cmp_huecorrect() node_type_size(&ntype, 320, 140, 500); node_type_init(&ntype, file_ns::node_composit_init_huecorrect); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc index 25ab9aa63fc..ac8456cb931 100644 --- a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** ID Mask ******************** */ @@ -26,6 +28,23 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "use_antialiasing", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class IDMaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("ID value").pass_through(get_result("Alpha")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new IDMaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_id_mask_cc void register_node_type_cmp_idmask() @@ -37,6 +56,7 @@ void register_node_type_cmp_idmask() cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_idmask_declare; ntype.draw_buttons = file_ns::node_composit_buts_id_mask; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc index d75aa575395..d8852e9333f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -8,6 +8,7 @@ #include "node_composite_util.hh" #include "BLI_linklist.h" +#include "BLI_math_vec_types.hh" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -17,6 +18,8 @@ #include "BKE_main.h" #include "BKE_scene.h" +#include "DEG_depsgraph_query.h" + #include "DNA_scene_types.h" #include "RE_engine.h" @@ -27,6 +30,12 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + /* **************** IMAGE (and RenderResult, multilayer image) ******************** */ static bNodeSocketTemplate cmp_node_rlayers_out[] = { @@ -433,6 +442,215 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree), } } +using namespace blender::realtime_compositor; + +class ImageOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + if (!is_valid()) { + allocate_invalid(); + return; + } + + update_image_frame_number(); + + for (const OutputSocketRef *output : node()->outputs()) { + compute_output(output->identifier()); + } + } + + /* Returns true if the node results can be computed, otherwise, returns false. */ + bool is_valid() + { + Image *image = get_image(); + ImageUser *image_user = get_image_user(); + if (!image || !image_user) { + return false; + } + + if (BKE_image_is_multilayer(image)) { + if (!image->rr) { + return false; + } + + RenderLayer *render_layer = get_render_layer(); + if (!render_layer) { + return false; + } + } + + return true; + } + + /* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */ + void allocate_invalid() + { + for (const OutputSocketRef *output : node()->outputs()) { + if (!should_compute_output(output->identifier())) { + continue; + } + + Result &result = get_result(output->identifier()); + result.allocate_invalid(); + } + } + + /* Compute the effective frame number of the image if it was animated and invalidate the cached + * GPU texture if the computed frame number is different. */ + void update_image_frame_number() + { + BKE_image_user_frame_calc(get_image(), get_image_user(), context().get_frame_number()); + } + + void compute_output(StringRef identifier) + { + if (!should_compute_output(identifier)) { + return; + } + + ImageUser image_user = compute_image_user_for_output(identifier); + GPUTexture *image_texture = BKE_image_get_gpu_texture(get_image(), &image_user, nullptr); + + const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture)); + Result &result = get_result(identifier); + result.allocate_texture(Domain(size)); + + GPUShader *shader = shader_manager().get(get_shader_name(identifier)); + GPU_shader_bind(shader); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(image_texture, input_unit); + + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + GPU_shader_unbind(); + GPU_texture_unbind(image_texture); + result.unbind_as_image(); + } + + /* Get a copy of the image user that is appropriate to retrieve the image buffer for the output + * with the given identifier. This essentially sets the appropriate pass and view indices that + * corresponds to the output. */ + ImageUser compute_image_user_for_output(StringRef identifier) + { + ImageUser image_user = *get_image_user(); + + /* Set the needed view. */ + image_user.view = get_view_index(); + + /* Set the needed pass. */ + if (BKE_image_is_multilayer(get_image())) { + image_user.pass = get_pass_index(get_pass_name(identifier)); + BKE_image_multilayer_index(get_image()->rr, &image_user); + } + else { + BKE_image_multiview_index(get_image(), &image_user); + } + + return image_user; + } + + /* Get the shader that should be used to compute the output with the given identifier. The + * shaders just copy the retrieved image textures into the results except for the alpha output, + * which extracts the alpha and writes it to the result instead. Note that a call to a host + * texture copy doesn't work because results are stored in a different half float formats. */ + const char *get_shader_name(StringRef identifier) + { + if (identifier == "Alpha") { + return "compositor_extract_alpha_from_color"; + } + else if (get_result(identifier).type() == ResultType::Color) { + return "compositor_convert_color_to_half_color"; + } + else { + return "compositor_convert_float_to_half_float"; + } + } + + Image *get_image() + { + return (Image *)bnode().id; + } + + ImageUser *get_image_user() + { + return static_cast<ImageUser *>(bnode().storage); + } + + /* Get the render layer selected in the node assuming the image is a multilayer image. */ + RenderLayer *get_render_layer() + { + const ListBase *layers = &get_image()->rr->layers; + return static_cast<RenderLayer *>(BLI_findlink(layers, get_image_user()->layer)); + } + + /* Get the name of the pass corresponding to the output with the given identifier assuming the + * image is a multilayer image. */ + const char *get_pass_name(StringRef identifier) + { + DOutputSocket output = node().output_by_identifier(identifier); + return static_cast<NodeImageLayer *>(output->bsocket()->storage)->pass_name; + } + + /* Get the index of the pass with the given name in the selected render layer's passes list + * assuming the image is a multilayer image. */ + int get_pass_index(const char *name) + { + return BLI_findstringindex(&get_render_layer()->passes, name, offsetof(RenderPass, name)); + } + + /* Get the index of the view selected in the node. If the image is not a multi-view image or only + * has a single view, then zero is returned. Otherwise, if the image is a multi-view image, the + * index of the selected view is returned. However, note that the value of the view member of the + * image user is not the actual index of the view. More specifically, the index 0 is reserved to + * denote the special mode of operation "All", which dynamically selects the view whose name + * matches the view currently being rendered. It follows that the views are then indexed starting + * from 1. So for non zero view values, the actual index of the view is the value of the view + * member of the image user minus 1. */ + int get_view_index() + { + /* The image is not a multi-view image, so just return zero. */ + if (!BKE_image_is_multiview(get_image())) { + return 0; + } + + const ListBase *views = &get_image()->rr->views; + /* There is only one view and its index is 0. */ + if (BLI_listbase_count_at_most(views, 2) < 2) { + return 0; + } + + const int view = get_image_user()->view; + /* The view is not zero, which means it is manually specified and the actual index is then the + * view value minus 1. */ + if (view != 0) { + return view - 1; + } + + /* Otherwise, the view value is zero, denoting the special mode of operation "All", which finds + * the index of the view whose name matches the view currently being rendered. */ + const char *view_name = context().get_view_name().data(); + const int matched_view = BLI_findstringindex(views, view_name, offsetof(RenderView, name)); + + /* No view matches the view currently being rendered, so fallback to the first view. */ + if (matched_view == -1) { + return 0; + } + + return matched_view; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ImageOperation(context, node); +} + } // namespace blender::nodes::node_composite_image_cc void register_node_type_cmp_image() @@ -446,6 +664,7 @@ void register_node_type_cmp_image() node_type_storage( &ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image); node_type_update(&ntype, file_ns::cmp_node_image_update); + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.labelfunc = node_image_label; ntype.flag |= NODE_PREVIEW; @@ -469,7 +688,7 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index) return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name; } -namespace blender::nodes::node_composite_image_cc { +namespace blender::nodes::node_composite_render_layer_cc { static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr) { @@ -595,11 +814,60 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer RNA_string_set(&op_ptr, "scene", scene_name); } -} // namespace blender::nodes::node_composite_image_cc +using namespace blender::realtime_compositor; + +class RenderLayerOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + const int view_layer = bnode().custom1; + GPUTexture *pass_texture = context().get_input_texture(view_layer, SCE_PASS_COMBINED); + const int2 size = int2(GPU_texture_width(pass_texture), GPU_texture_height(pass_texture)); + + /* Compute image output. */ + Result &image_result = get_result("Image"); + image_result.allocate_texture(Domain(size)); + GPU_texture_copy(image_result.texture(), pass_texture); + + /* Compute alpha output. */ + Result &alpha_result = get_result("Alpha"); + alpha_result.allocate_texture(Domain(size)); + + GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color"); + GPU_shader_bind(shader); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(pass_texture, input_unit); + + alpha_result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + GPU_shader_unbind(); + GPU_texture_unbind(pass_texture); + alpha_result.unbind_as_image(); + + /* Other output passes are not supported for now, so allocate them as invalid. */ + for (const OutputSocketRef *output : node()->outputs()) { + if (output->identifier() != "Image" && output->identifier() != "Alpha") { + get_result(output->identifier()).allocate_invalid(); + } + } + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new RenderLayerOperation(context, node); +} + +} // namespace blender::nodes::node_composite_render_layer_cc void register_node_type_cmp_rlayers() { - namespace file_ns = blender::nodes::node_composite_image_cc; + namespace file_ns = blender::nodes::node_composite_render_layer_cc; static bNodeType ntype; @@ -608,6 +876,7 @@ void register_node_type_cmp_rlayers() ntype.draw_buttons = file_ns::node_composit_buts_viewlayers; ntype.initfunc_api = file_ns::node_composit_init_rlayers; ntype.poll = file_ns::node_composit_poll_rlayers; + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.flag |= NODE_PREVIEW; node_type_storage( &ntype, nullptr, file_ns::node_composit_free_rlayers, file_ns::node_composit_copy_rlayers); diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc index 2958d1b2869..f6e46bef299 100644 --- a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc +++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Inpaint/ ******************** */ @@ -25,6 +27,23 @@ static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class InpaintOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new InpaintOperation(context, node); +} + } // namespace blender::nodes::node_composite_inpaint_cc void register_node_type_cmp_inpaint() @@ -36,6 +55,7 @@ void register_node_type_cmp_inpaint() cmp_node_type_base(&ntype, CMP_NODE_INPAINT, "Inpaint", NODE_CLASS_OP_FILTER); ntype.declare = file_ns::cmp_node_inpaint_declare; ntype.draw_buttons = file_ns::node_composit_buts_inpaint; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc index 6dff043537a..4bfcc7b6b9c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_invert.cc +++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** INVERT ******************** */ @@ -16,8 +20,15 @@ namespace blender::nodes::node_composite_invert_cc { static void cmp_node_invert_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Color")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Color")); } @@ -35,6 +46,45 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(col, ptr, "invert_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class InvertShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float do_rgb = get_do_rgb(); + const float do_alpha = get_do_alpha(); + + GPU_stack_link(material, + &bnode(), + "node_composite_invert", + inputs, + outputs, + GPU_constant(&do_rgb), + GPU_constant(&do_alpha)); + } + + bool get_do_rgb() + { + return bnode().custom1 & CMP_CHAN_RGB; + } + + bool get_do_alpha() + { + return bnode().custom1 & CMP_CHAN_A; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new InvertShaderNode(node); +} + } // namespace blender::nodes::node_composite_invert_cc void register_node_type_cmp_invert() @@ -47,6 +97,7 @@ void register_node_type_cmp_invert() ntype.declare = file_ns::cmp_node_invert_declare; ntype.draw_buttons = file_ns::node_composit_buts_invert; node_type_init(&ntype, file_ns::node_composit_init_invert); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.cc b/source/blender/nodes/composite/nodes/node_composite_keying.cc index fbfdf2ad3c6..8b584e216cd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keying.cc +++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc @@ -14,6 +14,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Keying ******************** */ @@ -63,6 +65,25 @@ static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "blur_post", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class KeyingOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Matte").allocate_invalid(); + get_result("Edges").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new KeyingOperation(context, node); +} + } // namespace blender::nodes::node_composite_keying_cc void register_node_type_cmp_keying() @@ -77,6 +98,7 @@ void register_node_type_cmp_keying() node_type_init(&ntype, file_ns::node_composit_init_keying); node_type_storage( &ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc index e835ee9e721..9eec705b6ca 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc +++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc @@ -21,6 +21,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Keying Screen ******************** */ @@ -78,6 +80,23 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point } } +using namespace blender::realtime_compositor; + +class KeyingScreenOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Screen").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new KeyingScreenOperation(context, node); +} + } // namespace blender::nodes::node_composite_keyingscreen_cc void register_node_type_cmp_keyingscreen() @@ -92,6 +111,7 @@ void register_node_type_cmp_keyingscreen() ntype.initfunc_api = file_ns::node_composit_init_keyingscreen; node_type_storage( &ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc index 593b7cc9b71..2d4c0afcda7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc +++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc @@ -5,20 +5,48 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" + #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" +/* Distortion can't be exactly -1.0 as it will cause infinite pincushion distortion. */ +#define MINIMUM_DISTORTION -0.999f +/* Arbitrary scaling factor for the dispersion input in projector distortion mode. */ +#define PROJECTOR_DISPERSION_SCALE 5.0f +/* Arbitrary scaling factor for the dispersion input in screen distortion mode. */ +#define SCREEN_DISPERSION_SCALE 4.0f +/* Arbitrary scaling factor for the distortion input. */ +#define DISTORTION_SCALE 4.0f + namespace blender::nodes::node_composite_lensdist_cc { static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Distort")).default_value(0.0f).min(-0.999f).max(1.0f); - b.add_input<decl::Float>(N_("Dispersion")).default_value(0.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Distort")) + .default_value(0.0f) + .min(MINIMUM_DISTORTION) + .max(1.0f) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Dispersion")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } @@ -42,6 +70,178 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "use_fit", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class LensDistortionOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + if (get_is_projector()) { + execute_projector_distortion(); + } + else { + execute_screen_distortion(); + } + } + + void execute_projector_distortion() + { + GPUShader *shader = shader_manager().get("compositor_projector_lens_distortion"); + GPU_shader_bind(shader); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + GPU_texture_filter_mode(input_image.texture(), true); + GPU_texture_wrap_mode(input_image.texture(), false, false); + + const Domain domain = compute_domain(); + + const float dispersion = (get_dispersion() * PROJECTOR_DISPERSION_SCALE) / domain.size.x; + GPU_shader_uniform_1f(shader, "dispersion", dispersion); + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + void execute_screen_distortion() + { + GPUShader *shader = shader_manager().get(get_screen_distortion_shader()); + GPU_shader_bind(shader); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + GPU_texture_filter_mode(input_image.texture(), true); + GPU_texture_wrap_mode(input_image.texture(), false, false); + + const Domain domain = compute_domain(); + + const float3 chromatic_distortion = compute_chromatic_distortion(); + GPU_shader_uniform_3fv(shader, "chromatic_distortion", chromatic_distortion); + + GPU_shader_uniform_1f(shader, "scale", compute_scale()); + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + const char *get_screen_distortion_shader() + { + if (get_is_jitter()) { + return "compositor_screen_lens_distortion_jitter"; + } + return "compositor_screen_lens_distortion"; + } + + float get_distortion() + { + const Result &input = get_input("Distort"); + return clamp_f(input.get_float_value_default(0.0f), MINIMUM_DISTORTION, 1.0f); + } + + float get_dispersion() + { + const Result &input = get_input("Dispersion"); + return clamp_f(input.get_float_value_default(0.0f), 0.0f, 1.0f); + } + + /* Get the distortion amount for each channel. The green channel has a distortion amount that + * matches that specified in the node inputs, while the red and blue channels have higher and + * lower distortion amounts respectively based on the dispersion value. */ + float3 compute_chromatic_distortion() + { + const float green_distortion = get_distortion(); + const float dispersion = get_dispersion() / SCREEN_DISPERSION_SCALE; + const float red_distortion = clamp_f(green_distortion + dispersion, MINIMUM_DISTORTION, 1.0f); + const float blue_distortion = clamp_f(green_distortion - dispersion, MINIMUM_DISTORTION, 1.0f); + return float3(red_distortion, green_distortion, blue_distortion) * DISTORTION_SCALE; + } + + /* The distortion model will distort the image in such a way that the result will no longer + * fit the domain of the original image, so we scale the image to account for that. If get_is_fit + * is false, then the scaling factor will be such that the furthest pixels horizontally and + * vertically are at the boundary of the image. Otherwise, if get_is_fit is true, the scaling + * factor will be such that the furthest pixels diagonally are at the corner of the image. */ + float compute_scale() + { + const float3 distortion = compute_chromatic_distortion() / DISTORTION_SCALE; + const float maximum_distortion = max_fff(distortion[0], distortion[1], distortion[2]); + + if (get_is_fit() && (maximum_distortion > 0.0f)) { + return 1.0f / (1.0f + 2.0f * maximum_distortion); + } + return 1.0f / (1.0f + maximum_distortion); + } + + bool get_is_projector() + { + return get_node_lens_distortion().proj; + } + + bool get_is_jitter() + { + return get_node_lens_distortion().jit; + } + + bool get_is_fit() + { + return get_node_lens_distortion().fit; + } + + NodeLensDist &get_node_lens_distortion() + { + return *static_cast<NodeLensDist *>(bnode().storage); + } + + /* Returns true if the operation does nothing and the input can be passed through. */ + bool is_identity() + { + /* The input is a single value and the operation does nothing. */ + if (get_input("Image").is_single_value()) { + return true; + } + + /* Projector have zero dispersion and does nothing. */ + if (get_is_projector() && get_dispersion() == 0.0f) { + return true; + } + + /* Both distortion and dispersion are zero and the operation does nothing. */ + if (get_distortion() == 0.0f && get_dispersion() == 0.0f) { + return true; + } + + return false; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new LensDistortionOperation(context, node); +} + } // namespace blender::nodes::node_composite_lensdist_cc void register_node_type_cmp_lensdist() @@ -56,6 +256,7 @@ void register_node_type_cmp_lensdist() node_type_init(&ntype, file_ns::node_composit_init_lensdist); node_type_storage( &ntype, "NodeLensDist", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc index a30567672f0..2f1ebeb79b5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.cc +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** LEVELS ******************** */ @@ -31,6 +33,24 @@ static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C) uiItemR(layout, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class LevelsOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Mean").allocate_invalid(); + get_result("Std Dev").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new LevelsOperation(context, node); +} + } // namespace blender::nodes::node_composite_levels_cc void register_node_type_cmp_view_levels() @@ -44,6 +64,7 @@ void register_node_type_cmp_view_levels() ntype.draw_buttons = file_ns::node_composit_buts_view_levels; ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_view_levels); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc index 94697a2aafd..092a12a7ea4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc @@ -5,9 +5,15 @@ * \ingroup cmpnodes */ +#include "IMB_colormanagement.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Luma Matte Node ********************************* */ @@ -16,7 +22,9 @@ namespace blender::nodes::node_composite_luma_matte_cc { static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -40,6 +48,53 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class LuminanceMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float high = get_high(); + const float low = get_low(); + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + + GPU_stack_link(material, + &bnode(), + "node_composite_luminance_matte", + inputs, + outputs, + GPU_uniform(&high), + GPU_uniform(&low), + GPU_constant(luminance_coefficients)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + float get_high() + { + return get_node_chroma()->t1; + } + + float get_low() + { + return get_node_chroma()->t2; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new LuminanceMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_luma_matte_cc void register_node_type_cmp_luma_matte() @@ -54,6 +109,7 @@ void register_node_type_cmp_luma_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_luma_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_map_range.cc b/source/blender/nodes/composite/nodes/node_composite_map_range.cc index e52c6d096b9..e72869efa93 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_range.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_range.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Map Range ******************** */ @@ -16,11 +20,31 @@ namespace blender::nodes::node_composite_map_range_cc { static void cmp_node_map_range_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("From Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("From Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("To Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("To Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Value")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("From Min")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(1); + b.add_input<decl::Float>(N_("From Max")) + .default_value(1.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(2); + b.add_input<decl::Float>(N_("To Min")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(3); + b.add_input<decl::Float>(N_("To Max")) + .default_value(1.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(4); b.add_output<decl::Float>(N_("Value")); } @@ -32,6 +56,38 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class MapRangeShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float should_clamp = get_should_clamp(); + + GPU_stack_link(material, + &bnode(), + "node_composite_map_range", + inputs, + outputs, + GPU_constant(&should_clamp)); + } + + bool get_should_clamp() + { + return bnode().custom1; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new MapRangeShaderNode(node); +} + } // namespace blender::nodes::node_composite_map_range_cc void register_node_type_cmp_map_range() @@ -43,6 +99,7 @@ void register_node_type_cmp_map_range() cmp_node_type_base(&ntype, CMP_NODE_MAP_RANGE, "Map Range", NODE_CLASS_OP_VECTOR); ntype.declare = file_ns::cmp_node_map_range_declare; ntype.draw_buttons = file_ns::node_composit_buts_map_range; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc index 31961f07ea4..4f660d62c3b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Map UV ******************** */ @@ -26,6 +28,23 @@ static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class MapUVOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new MapUVOperation(context, node); +} + } // namespace blender::nodes::node_composite_map_uv_cc void register_node_type_cmp_mapuv() @@ -37,6 +56,7 @@ void register_node_type_cmp_mapuv() cmp_node_type_base(&ntype, CMP_NODE_MAP_UV, "Map UV", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_map_uv_declare; ntype.draw_buttons = file_ns::node_composit_buts_map_uv; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc index bb42628ed3d..ec9b2d56636 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc @@ -12,6 +12,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** MAP VALUE ******************** */ @@ -20,7 +24,11 @@ namespace blender::nodes::node_composite_map_value_cc { static void cmp_node_map_value_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Value")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Value")); } @@ -50,6 +58,56 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), uiItemR(sub, ptr, "max", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class MapValueShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const TexMapping *texture_mapping = get_texture_mapping(); + + const float use_min = get_use_min(); + const float use_max = get_use_max(); + + GPU_stack_link(material, + &bnode(), + "node_composite_map_value", + inputs, + outputs, + GPU_uniform(texture_mapping->loc), + GPU_uniform(texture_mapping->size), + GPU_constant(&use_min), + GPU_uniform(texture_mapping->min), + GPU_constant(&use_max), + GPU_uniform(texture_mapping->max)); + } + + TexMapping *get_texture_mapping() + { + return static_cast<TexMapping *>(bnode().storage); + } + + bool get_use_min() + { + return get_texture_mapping()->flag & TEXMAP_CLIP_MIN; + } + + bool get_use_max() + { + return get_texture_mapping()->flag & TEXMAP_CLIP_MAX; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new MapValueShaderNode(node); +} + } // namespace blender::nodes::node_composite_map_value_cc void register_node_type_cmp_map_value() @@ -63,6 +121,7 @@ void register_node_type_cmp_map_value() ntype.draw_buttons = file_ns::node_composit_buts_map_value; node_type_init(&ntype, file_ns::node_composit_init_map_value); node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc index 5b8fac5d1c0..2372dbae3f2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Mask ******************** */ @@ -74,6 +76,23 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p } } +using namespace blender::realtime_compositor; + +class MaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Mask").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new MaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_mask_cc void register_node_type_cmp_mask() @@ -87,6 +106,7 @@ void register_node_type_cmp_mask() ntype.draw_buttons = file_ns::node_composit_buts_mask; node_type_init(&ntype, file_ns::node_composit_init_mask); ntype.labelfunc = file_ns::node_mask_label; + ntype.get_compositor_operation = file_ns::get_compositor_operation; node_type_storage(&ntype, "NodeMask", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_math.cc b/source/blender/nodes/composite/nodes/node_composite_math.cc index 7b2eadef2cb..4baf057913e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.cc +++ b/source/blender/nodes/composite/nodes/node_composite_math.cc @@ -5,6 +5,12 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + +#include "NOD_math_functions.hh" + #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ @@ -13,18 +19,72 @@ namespace blender::nodes::node_composite_math_cc { static void cmp_node_math_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Value")) + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(0); b.add_input<decl::Float>(N_("Value"), "Value_001") .default_value(0.5f) .min(-10000.0f) - .max(10000.0f); + .max(10000.0f) + .compositor_domain_priority(1); b.add_input<decl::Float>(N_("Value"), "Value_002") .default_value(0.5f) .min(-10000.0f) - .max(10000.0f); + .max(10000.0f) + .compositor_domain_priority(2); b.add_output<decl::Float>(N_("Value")); } +using namespace blender::realtime_compositor; + +class MathShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + + if (!get_should_clamp()) { + return; + } + + const float min = 0.0f; + const float max = 1.0f; + GPU_link(material, + "clamp_value", + get_output("Value").link, + GPU_constant(&min), + GPU_constant(&max), + &get_output("Value").link); + } + + NodeMathOperation get_operation() + { + return (NodeMathOperation)bnode().custom1; + } + + const char *get_shader_function_name() + { + return get_float_math_operation_info(get_operation())->shader_name.c_str(); + } + + bool get_should_clamp() + { + return bnode().custom2 & SHD_MATH_CLAMP; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new MathShaderNode(node); +} + } // namespace blender::nodes::node_composite_math_cc void register_node_type_cmp_math() @@ -37,6 +97,7 @@ void register_node_type_cmp_math() ntype.declare = file_ns::cmp_node_math_declare; ntype.labelfunc = node_math_label; node_type_update(&ntype, node_math_update); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc index fc11aa188b0..a1fbbfe7d40 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc @@ -5,6 +5,14 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" + +#include "DNA_material_types.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** MIX RGB ******************** */ @@ -13,12 +21,122 @@ namespace blender::nodes::node_composite_mixrgb_cc { static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(2); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Image"), "Image_001") + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class MixRGBShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + if (get_use_alpha()) { + GPU_link(material, + "multiply_by_alpha", + get_input_link("Fac"), + get_input_link("Image_001"), + &get_input("Fac").link); + } + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + + if (!get_should_clamp()) { + return; + } + + const float min[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float max[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + GPU_link(material, + "clamp_color", + get_output("Image").link, + GPU_constant(min), + GPU_constant(max), + &get_output("Image").link); + } + + int get_mode() + { + return bnode().custom1; + } + + const char *get_shader_function_name() + { + switch (get_mode()) { + case MA_RAMP_BLEND: + return "mix_blend"; + case MA_RAMP_ADD: + return "mix_add"; + case MA_RAMP_MULT: + return "mix_mult"; + case MA_RAMP_SUB: + return "mix_sub"; + case MA_RAMP_SCREEN: + return "mix_screen"; + case MA_RAMP_DIV: + return "mix_div"; + case MA_RAMP_DIFF: + return "mix_diff"; + case MA_RAMP_DARK: + return "mix_dark"; + case MA_RAMP_LIGHT: + return "mix_light"; + case MA_RAMP_OVERLAY: + return "mix_overlay"; + case MA_RAMP_DODGE: + return "mix_dodge"; + case MA_RAMP_BURN: + return "mix_burn"; + case MA_RAMP_HUE: + return "mix_hue"; + case MA_RAMP_SAT: + return "mix_sat"; + case MA_RAMP_VAL: + return "mix_val"; + case MA_RAMP_COLOR: + return "mix_color"; + case MA_RAMP_SOFT: + return "mix_soft"; + case MA_RAMP_LINEAR: + return "mix_linear"; + } + + BLI_assert_unreachable(); + return nullptr; + } + + bool get_use_alpha() + { + return bnode().custom2 & SHD_MIXRGB_USE_ALPHA; + } + + bool get_should_clamp() + { + return bnode().custom2 & SHD_MIXRGB_CLAMP; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new MixRGBShaderNode(node); +} + } // namespace blender::nodes::node_composite_mixrgb_cc void register_node_type_cmp_mix_rgb() @@ -31,6 +149,7 @@ void register_node_type_cmp_mix_rgb() ntype.flag |= NODE_PREVIEW; ntype.declare = file_ns::cmp_node_mixrgb_declare; ntype.labelfunc = node_blend_label; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc index a4d5f294fe0..ec95de3da18 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -5,8 +5,13 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + #include "BKE_context.h" #include "BKE_lib_id.h" +#include "BKE_movieclip.h" +#include "BKE_tracking.h" + #include "DNA_defaults.h" #include "RNA_access.h" @@ -14,6 +19,12 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_movieclip_cc { @@ -79,6 +90,177 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings"); } +using namespace blender::realtime_compositor; + +class MovieClipOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUTexture *movie_clip_texture = get_movie_clip_texture(); + + compute_image(movie_clip_texture); + compute_alpha(movie_clip_texture); + compute_stabilization_data(movie_clip_texture); + + free_movie_clip_texture(); + } + + void compute_image(GPUTexture *movie_clip_texture) + { + if (!should_compute_output("Image")) { + return; + } + + Result &result = get_result("Image"); + + /* The movie clip texture is invalid or missing, set an appropriate fallback value. */ + if (!movie_clip_texture) { + result.allocate_invalid(); + return; + } + + const int2 size = int2(GPU_texture_width(movie_clip_texture), + GPU_texture_height(movie_clip_texture)); + result.allocate_texture(Domain(size)); + + GPUShader *shader = shader_manager().get("compositor_convert_color_to_half_color"); + GPU_shader_bind(shader); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(movie_clip_texture, input_unit); + + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + GPU_shader_unbind(); + GPU_texture_unbind(movie_clip_texture); + result.unbind_as_image(); + } + + void compute_alpha(GPUTexture *movie_clip_texture) + { + if (!should_compute_output("Alpha")) { + return; + } + + Result &result = get_result("Alpha"); + + /* The movie clip texture is invalid or missing, set an appropriate fallback value. */ + if (!movie_clip_texture) { + result.allocate_single_value(); + result.set_float_value(1.0f); + return; + } + + const int2 size = int2(GPU_texture_width(movie_clip_texture), + GPU_texture_height(movie_clip_texture)); + result.allocate_texture(Domain(size)); + + GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color"); + GPU_shader_bind(shader); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(movie_clip_texture, input_unit); + + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + GPU_shader_unbind(); + GPU_texture_unbind(movie_clip_texture); + result.unbind_as_image(); + } + + void compute_stabilization_data(GPUTexture *movie_clip_texture) + { + /* The movie clip texture is invalid or missing, set appropriate fallback values. */ + if (!movie_clip_texture) { + if (should_compute_output("Offset X")) { + Result &result = get_result("Offset X"); + result.allocate_single_value(); + result.set_float_value(0.0f); + } + if (should_compute_output("Offset Y")) { + Result &result = get_result("Offset Y"); + result.allocate_single_value(); + result.set_float_value(0.0f); + } + if (should_compute_output("Scale")) { + Result &result = get_result("Scale"); + result.allocate_single_value(); + result.set_float_value(1.0f); + } + if (should_compute_output("Angle")) { + Result &result = get_result("Angle"); + result.allocate_single_value(); + result.set_float_value(0.0f); + } + return; + } + + MovieClip *movie_clip = get_movie_clip(); + const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip, + context().get_frame_number()); + const int width = GPU_texture_width(movie_clip_texture); + const int height = GPU_texture_height(movie_clip_texture); + + /* If the movie clip has no stabilization data, it will initialize the given values with + * fallback values regardless, so no need to handle that case. */ + float2 offset; + float scale, angle; + BKE_tracking_stabilization_data_get( + movie_clip, frame_number, width, height, offset, &scale, &angle); + + if (should_compute_output("Offset X")) { + Result &result = get_result("Offset X"); + result.allocate_single_value(); + result.set_float_value(offset.x); + } + if (should_compute_output("Offset Y")) { + Result &result = get_result("Offset Y"); + result.allocate_single_value(); + result.set_float_value(offset.y); + } + if (should_compute_output("Scale")) { + Result &result = get_result("Scale"); + result.allocate_single_value(); + result.set_float_value(scale); + } + if (should_compute_output("Angle")) { + Result &result = get_result("Angle"); + result.allocate_single_value(); + result.set_float_value(angle); + } + } + + GPUTexture *get_movie_clip_texture() + { + MovieClip *movie_clip = get_movie_clip(); + MovieClipUser *movie_clip_user = static_cast<MovieClipUser *>(bnode().storage); + BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number()); + return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user); + } + + void free_movie_clip_texture() + { + MovieClip *movie_clip = get_movie_clip(); + return BKE_movieclip_free_gputexture(movie_clip); + } + + MovieClip *get_movie_clip() + { + return (MovieClip *)bnode().id; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new MovieClipOperation(context, node); +} + } // namespace blender::nodes::node_composite_movieclip_cc void register_node_type_cmp_movieclip() @@ -91,6 +273,7 @@ void register_node_type_cmp_movieclip() ntype.declare = file_ns::cmp_node_movieclip_declare; ntype.draw_buttons = file_ns::node_composit_buts_movieclip; ntype.draw_buttons_ex = file_ns::node_composit_buts_movieclip_ex; + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.initfunc_api = file_ns::init; ntype.flag |= NODE_PREVIEW; node_type_storage( diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc index 4d52a767b8a..88638586594 100644 --- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc +++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc @@ -12,6 +12,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -81,6 +83,23 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po uiItemR(layout, ptr, "distortion_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class MovieDistortionOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new MovieDistortionOperation(context, node); +} + } // namespace blender::nodes::node_composite_moviedistortion_cc void register_node_type_cmp_moviedistortion() @@ -95,6 +114,7 @@ void register_node_type_cmp_moviedistortion() ntype.labelfunc = file_ns::label; ntype.initfunc_api = file_ns::init; node_type_storage(&ntype, nullptr, file_ns::storage_free, file_ns::storage_copy); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc index b4dd0bbacd0..f61ace01cfd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** NORMAL ******************** */ @@ -17,7 +21,8 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b) .default_value({0.0f, 0.0f, 1.0f}) .min(-1.0f) .max(1.0f) - .subtype(PROP_DIRECTION); + .subtype(PROP_DIRECTION) + .compositor_domain_priority(0); b.add_output<decl::Vector>(N_("Normal")) .default_value({0.0f, 0.0f, 1.0f}) .min(-1.0f) @@ -26,6 +31,37 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Dot")); } +using namespace blender::realtime_compositor; + +class NormalShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, + &bnode(), + "node_composite_normal", + inputs, + outputs, + GPU_uniform(get_vector_value())); + } + + /* The vector value is stored in the default value of the output socket. */ + float *get_vector_value() + { + return node().output_by_identifier("Normal")->default_value<bNodeSocketValueVector>()->value; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new NormalShaderNode(node); +} + } // namespace blender::nodes::node_composite_normal_cc void register_node_type_cmp_normal() @@ -36,6 +72,7 @@ void register_node_type_cmp_normal() cmp_node_type_base(&ntype, CMP_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR); ntype.declare = file_ns::cmp_node_normal_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.cc b/source/blender/nodes/composite/nodes/node_composite_normalize.cc index 49318279bdb..21765825468 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normalize.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** NORMALIZE single channel, useful for Z buffer ******************** */ @@ -17,6 +19,23 @@ static void cmp_node_normalize_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Value")); } +using namespace blender::realtime_compositor; + +class NormalizeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Value").pass_through(get_result("Value")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new NormalizeOperation(context, node); +} + } // namespace blender::nodes::node_composite_normalize_cc void register_node_type_cmp_normalize() @@ -27,6 +46,7 @@ void register_node_type_cmp_normalize() cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR); ntype.declare = file_ns::cmp_node_normalize_declare; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc index 84235b085a4..5ed383977a5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc +++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc @@ -24,6 +24,8 @@ #include "IMB_openexr.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** OUTPUT FILE ******************** */ @@ -439,6 +441,22 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi } } +using namespace blender::realtime_compositor; + +class OutputFileOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new OutputFileOperation(context, node); +} + } // namespace blender::nodes::node_composite_output_file_cc void register_node_type_cmp_output_file() @@ -455,6 +473,7 @@ void register_node_type_cmp_output_file() node_type_storage( &ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file); node_type_update(&ntype, file_ns::update_output_file); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc index 529aa0f84de..4567464a547 100644 --- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Pixelate ******************** */ @@ -17,6 +19,23 @@ static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b) b.add_output<decl::Color>(N_("Color")); } +using namespace blender::realtime_compositor; + +class PixelateOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Color").pass_through(get_result("Color")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new PixelateOperation(context, node); +} + } // namespace blender::nodes::node_composite_pixelate_cc void register_node_type_cmp_pixelate() @@ -27,6 +46,7 @@ void register_node_type_cmp_pixelate() cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER); ntype.declare = file_ns::cmp_node_pixelate_declare; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc index 6557478fc4b..68dc020a02e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc +++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc @@ -18,6 +18,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_planetrackdeform_cc { @@ -107,6 +109,24 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P } } +using namespace blender::realtime_compositor; + +class PlaneTrackDeformOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Plane").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new PlaneTrackDeformOperation(context, node); +} + } // namespace blender::nodes::node_composite_planetrackdeform_cc void register_node_type_cmp_planetrackdeform() @@ -121,6 +141,7 @@ void register_node_type_cmp_planetrackdeform() ntype.initfunc_api = file_ns::init; node_type_storage( &ntype, "NodePlaneTrackDeformData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.cc b/source/blender/nodes/composite/nodes/node_composite_posterize.cc index c97035d55ea..1268219e7e2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_posterize.cc +++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Posterize ******************** */ @@ -13,11 +17,37 @@ namespace blender::nodes::node_composite_posterize_cc { static void cmp_node_posterize_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Steps")).default_value(8.0f).min(2.0f).max(1024.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Steps")) + .default_value(8.0f) + .min(2.0f) + .max(1024.0f) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class PosterizeShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_posterize", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new PosterizeShaderNode(node); +} + } // namespace blender::nodes::node_composite_posterize_cc void register_node_type_cmp_posterize() @@ -28,6 +58,7 @@ void register_node_type_cmp_posterize() cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_posterize_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc index 000cc9df90a..c814ea5f738 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Premul and Key Alpha Convert ******************** */ @@ -16,7 +20,9 @@ namespace blender::nodes::node_composite_premulkey_cc { static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -25,6 +31,36 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "mapping", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class AlphaConvertShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + if (get_mode() == 0) { + GPU_stack_link(material, &bnode(), "color_alpha_premultiply", inputs, outputs); + return; + } + + GPU_stack_link(material, &bnode(), "color_alpha_unpremultiply", inputs, outputs); + } + + CMPNodeAlphaConvertMode get_mode() + { + return (CMPNodeAlphaConvertMode)bnode().custom1; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new AlphaConvertShaderNode(node); +} + } // namespace blender::nodes::node_composite_premulkey_cc void register_node_type_cmp_premulkey() @@ -36,6 +72,7 @@ void register_node_type_cmp_premulkey() cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_premulkey_declare; ntype.draw_buttons = file_ns::node_composit_buts_premulkey; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc index 5bc4c67dd8e..6f3a00af7e3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -5,6 +5,12 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + +#include "DNA_node_types.h" + +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** RGB ******************** */ @@ -16,6 +22,29 @@ static void cmp_node_rgb_declare(NodeDeclarationBuilder &b) b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); } +using namespace blender::realtime_compositor; + +class RGBOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &result = get_result("RGBA"); + result.allocate_single_value(); + + const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first); + float4 color = float4(static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value); + + result.set_color_value(color); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new RGBOperation(context, node); +} + } // namespace blender::nodes::node_composite_rgb_cc void register_node_type_cmp_rgb() @@ -27,6 +56,7 @@ void register_node_type_cmp_rgb() cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT); ntype.declare = file_ns::cmp_node_rgb_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.cc b/source/blender/nodes/composite/nodes/node_composite_rotate.cc index a083bc1837b..35caa3cd242 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rotate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc @@ -5,9 +5,14 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" +#include "BLI_float3x3.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Rotate ******************** */ @@ -16,12 +21,15 @@ namespace blender::nodes::node_composite_rotate_cc { static void cmp_node_rotate_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_input<decl::Float>(N_("Degr")) .default_value(0.0f) .min(-10000.0f) .max(10000.0f) - .subtype(PROP_ANGLE); + .subtype(PROP_ANGLE) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } @@ -35,6 +43,47 @@ static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class RotateOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input("Image"); + Result &result = get_result("Image"); + input.pass_through(result); + + const float rotation = get_input("Degr").get_float_value_default(0.0f); + + const float3x3 transformation = float3x3::from_rotation(rotation); + + result.transform(transformation); + result.get_realization_options().interpolation = get_interpolation(); + } + + Interpolation get_interpolation() + { + switch (bnode().custom1) { + case 0: + return Interpolation::Nearest; + case 1: + return Interpolation::Bilinear; + case 2: + return Interpolation::Bicubic; + } + + BLI_assert_unreachable(); + return Interpolation::Nearest; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new RotateOperation(context, node); +} + } // namespace blender::nodes::node_composite_rotate_cc void register_node_type_cmp_rotate() @@ -47,6 +96,7 @@ void register_node_type_cmp_rotate() ntype.declare = file_ns::cmp_node_rotate_declare; ntype.draw_buttons = file_ns::node_composit_buts_rotate; node_type_init(&ntype, file_ns::node_composit_init_rotate); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc index b2b42a3613c..8b43ae8c9ca 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Scale ******************** */ @@ -55,6 +57,23 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin } } +using namespace blender::realtime_compositor; + +class ScaleOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ScaleOperation(context, node); +} + } // namespace blender::nodes::node_composite_scale_cc void register_node_type_cmp_scale() @@ -67,6 +86,7 @@ void register_node_type_cmp_scale() ntype.declare = file_ns::cmp_node_scale_declare; ntype.draw_buttons = file_ns::node_composit_buts_scale; node_type_update(&ntype, file_ns::node_composite_update_scale); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc index 20bafb0d3d4..1f5317378bb 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc @@ -3,6 +3,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes { @@ -13,6 +15,38 @@ static void cmp_node_scene_time_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Frame")); } +using namespace blender::realtime_compositor; + +class SceneTimeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + execute_seconds(); + execute_frame(); + } + + void execute_seconds() + { + Result &result = get_result("Seconds"); + result.allocate_single_value(); + result.set_float_value(context().get_time()); + } + + void execute_frame() + { + Result &result = get_result("Frame"); + result.allocate_single_value(); + result.set_float_value(static_cast<float>(context().get_frame_number())); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new SceneTimeOperation(context, node); +} + } // namespace blender::nodes void register_node_type_cmp_scene_time() @@ -21,5 +55,7 @@ void register_node_type_cmp_scene_time() cmp_node_type_base(&ntype, CMP_NODE_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT); ntype.declare = blender::nodes::cmp_node_scene_time_declare; + ntype.get_compositor_operation = blender::nodes::get_compositor_operation; + nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc index b253656a628..d1f0b7977f8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc @@ -1,5 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_assert.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -58,7 +64,9 @@ namespace blender::nodes::node_composite_separate_color_cc { static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Red")); b.add_output<decl::Float>(N_("Green")); b.add_output<decl::Float>(N_("Blue")); @@ -71,6 +79,57 @@ static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode); } +using namespace blender::realtime_compositor; + +class SeparateColorShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + } + + NodeCMPCombSepColor *get_node_combine_separate_color() + { + return static_cast<NodeCMPCombSepColor *>(bnode().storage); + } + + const char *get_shader_function_name() + { + switch (get_node_combine_separate_color()->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: + return "node_composite_separate_rgba"; + case CMP_NODE_COMBSEP_COLOR_HSV: + return "node_composite_separate_hsva"; + case CMP_NODE_COMBSEP_COLOR_HSL: + return "node_composite_separate_hsla"; + case CMP_NODE_COMBSEP_COLOR_YUV: + return "node_composite_separate_yuva_itu_709"; + case CMP_NODE_COMBSEP_COLOR_YCC: + switch (get_node_combine_separate_color()->ycc_mode) { + case BLI_YCC_ITU_BT601: + return "node_composite_separate_ycca_itu_601"; + case BLI_YCC_ITU_BT709: + return "node_composite_separate_ycca_itu_709"; + case BLI_YCC_JFIF_0_255: + return "node_composite_separate_ycca_jpeg"; + } + } + + BLI_assert_unreachable(); + return nullptr; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateColorShaderNode(node); +} + } // namespace blender::nodes::node_composite_separate_color_cc void register_node_type_cmp_separate_color() @@ -85,6 +144,7 @@ void register_node_type_cmp_separate_color() node_type_storage( &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); node_type_update(&ntype, file_ns::cmp_node_separate_color_update); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } @@ -95,22 +155,30 @@ namespace blender::nodes::node_composite_combine_color_cc { static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Red")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(0); b.add_input<decl::Float>(N_("Green")) .default_value(0.0f) .min(0.0f) .max(1.0f) - .subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); b.add_input<decl::Float>(N_("Blue")) .default_value(0.0f) .min(0.0f) .max(1.0f) - .subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(2); b.add_input<decl::Float>(N_("Alpha")) .default_value(1.0f) .min(0.0f) .max(1.0f) - .subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } @@ -120,6 +188,57 @@ static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node) node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode); } +using namespace blender::realtime_compositor; + +class CombineColorShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + } + + NodeCMPCombSepColor *get_node_combine_separate_color() + { + return static_cast<NodeCMPCombSepColor *>(bnode().storage); + } + + const char *get_shader_function_name() + { + switch (get_node_combine_separate_color()->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: + return "node_composite_combine_rgba"; + case CMP_NODE_COMBSEP_COLOR_HSV: + return "node_composite_combine_hsva"; + case CMP_NODE_COMBSEP_COLOR_HSL: + return "node_composite_combine_hsla"; + case CMP_NODE_COMBSEP_COLOR_YUV: + return "node_composite_combine_yuva_itu_709"; + case CMP_NODE_COMBSEP_COLOR_YCC: + switch (get_node_combine_separate_color()->ycc_mode) { + case BLI_YCC_ITU_BT601: + return "node_composite_combine_ycca_itu_601"; + case BLI_YCC_ITU_BT709: + return "node_composite_combine_ycca_itu_709"; + case BLI_YCC_JFIF_0_255: + return "node_composite_combine_ycca_jpeg"; + } + } + + BLI_assert_unreachable(); + return nullptr; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineColorShaderNode(node); +} + } // namespace blender::nodes::node_composite_combine_color_cc void register_node_type_cmp_combine_color() @@ -134,6 +253,7 @@ void register_node_type_cmp_combine_color() node_type_storage( &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); node_type_update(&ntype, file_ns::cmp_node_combine_color_update); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc index a169f7e0dd3..b655c0db106 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -5,60 +5,112 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE HSVA ******************** */ -namespace blender::nodes::node_composite_sepcomb_hsva_cc { +namespace blender::nodes::node_composite_separate_hsva_cc { static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("H")); b.add_output<decl::Float>(N_("S")); b.add_output<decl::Float>(N_("V")); b.add_output<decl::Float>(N_("A")); } -} // namespace blender::nodes::node_composite_sepcomb_hsva_cc +using namespace blender::realtime_compositor; + +class SeparateHSVAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_separate_hsva", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateHSVAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_hsva_cc void register_node_type_cmp_sephsva() { - namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc; + namespace file_ns = blender::nodes::node_composite_separate_hsva_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sephsva_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE HSVA ******************** */ -namespace blender::nodes::node_composite_sepcomb_hsva_cc { +namespace blender::nodes::node_composite_combine_hsva_cc { static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f).compositor_domain_priority(0); + b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f).compositor_domain_priority(1); + b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2); + b.add_input<decl::Float>(N_("A")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes::node_composite_sepcomb_hsva_cc +using namespace blender::realtime_compositor; + +class CombineHSVAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_combine_hsva", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineHSVAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_hsva_cc void register_node_type_cmp_combhsva() { - namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc; + namespace file_ns = blender::nodes::node_composite_combine_hsva_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combhsva_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc index a243500b56d..1f4c9fd153f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -5,59 +5,112 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE RGBA ******************** */ -namespace blender::nodes::node_composite_sepcomb_rgba_cc { + +namespace blender::nodes::node_composite_separate_rgba_cc { static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("R")); b.add_output<decl::Float>(N_("G")); b.add_output<decl::Float>(N_("B")); b.add_output<decl::Float>(N_("A")); } -} // namespace blender::nodes::node_composite_sepcomb_rgba_cc +using namespace blender::realtime_compositor; + +class SeparateRGBAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_separate_rgba", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateRGBAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_rgba_cc void register_node_type_cmp_seprgba() { - namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc; + namespace file_ns = blender::nodes::node_composite_separate_rgba_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_seprgba_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE RGBA ******************** */ -namespace blender::nodes::node_composite_sepcomb_rgba_cc { +namespace blender::nodes::node_composite_combine_rgba_cc { static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f).compositor_domain_priority(0); + b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f).compositor_domain_priority(1); + b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f).compositor_domain_priority(2); + b.add_input<decl::Float>(N_("A")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes::node_composite_sepcomb_rgba_cc +using namespace blender::realtime_compositor; + +class CombineRGBAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_combine_rgba", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineRGBAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_rgba_cc void register_node_type_cmp_combrgba() { - namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc; + namespace file_ns = blender::nodes::node_composite_combine_rgba_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combrgba_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc index 4979c376cab..e288e698808 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc @@ -5,10 +5,15 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE XYZ ******************** */ -namespace blender::nodes { + +namespace blender::nodes::node_composite_separate_xyz_cc { static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b) { @@ -18,21 +23,44 @@ static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>("Z"); } -} // namespace blender::nodes +using namespace blender::realtime_compositor; + +class SeparateXYZShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_separate_xyz", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateXYZShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_xyz_cc void register_node_type_cmp_separate_xyz() { + namespace file_ns = blender::nodes::node_composite_separate_xyz_cc; + static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_XYZ, "Separate XYZ", NODE_CLASS_CONVERTER); - ntype.declare = blender::nodes::cmp_node_separate_xyz_declare; + ntype.declare = file_ns::cmp_node_separate_xyz_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE XYZ ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_combine_xyz_cc { static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b) { @@ -42,14 +70,37 @@ static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>("Vector"); } -} // namespace blender::nodes +using namespace blender::realtime_compositor; + +class CombineXYZShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_combine_xyz", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineXYZShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_xyz_cc void register_node_type_cmp_combine_xyz() { + namespace file_ns = blender::nodes::node_composite_combine_xyz_cc; + static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBINE_XYZ, "Combine XYZ", NODE_CLASS_CONVERTER); - ntype.declare = blender::nodes::cmp_node_combine_xyz_declare; + ntype.declare = file_ns::cmp_node_combine_xyz_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc index 51d3c18d238..bebe6abe115 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -5,15 +5,23 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE YCCA ******************** */ -namespace blender::nodes::node_composite_sepcomb_ycca_cc { +namespace blender::nodes::node_composite_separate_ycca_cc { static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Y")); b.add_output<decl::Float>(N_("Cb")); b.add_output<decl::Float>(N_("Cr")); @@ -25,11 +33,51 @@ static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *nod node->custom1 = 1; /* BLI_YCC_ITU_BT709 */ } -} // namespace blender::nodes::node_composite_sepcomb_ycca_cc +using namespace blender::realtime_compositor; + +class SeparateYCCAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + } + + int get_mode() + { + return bnode().custom1; + } + + const char *get_shader_function_name() + { + switch (get_mode()) { + case BLI_YCC_ITU_BT601: + return "node_composite_separate_ycca_itu_601"; + case BLI_YCC_ITU_BT709: + return "node_composite_separate_ycca_itu_709"; + case BLI_YCC_JFIF_0_255: + return "node_composite_separate_ycca_jpeg"; + } + + BLI_assert_unreachable(); + return nullptr; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateYCCAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_ycca_cc void register_node_type_cmp_sepycca() { - namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc; + namespace file_ns = blender::nodes::node_composite_separate_ycca_cc; static bNodeType ntype; @@ -37,20 +85,33 @@ void register_node_type_cmp_sepycca() ntype.declare = file_ns::cmp_node_sepycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca); ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE YCCA ******************** */ -namespace blender::nodes::node_composite_sepcomb_ycca_cc { +namespace blender::nodes::node_composite_combine_ycca_cc { static void cmp_node_combycca_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Cb")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(1); + b.add_input<decl::Float>(N_("Cr")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(2); + b.add_input<decl::Float>(N_("A")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } @@ -59,11 +120,51 @@ static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *no node->custom1 = 1; /* BLI_YCC_ITU_BT709 */ } -} // namespace blender::nodes::node_composite_sepcomb_ycca_cc +using namespace blender::realtime_compositor; + +class CombineYCCAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + } + + int get_mode() + { + return bnode().custom1; + } + + const char *get_shader_function_name() + { + switch (get_mode()) { + case BLI_YCC_ITU_BT601: + return "node_composite_combine_ycca_itu_601"; + case BLI_YCC_ITU_BT709: + return "node_composite_combine_ycca_itu_709"; + case BLI_YCC_JFIF_0_255: + return "node_composite_combine_ycca_jpeg"; + } + + BLI_assert_unreachable(); + return nullptr; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineYCCAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_ycca_cc void register_node_type_cmp_combycca() { - namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc; + namespace file_ns = blender::nodes::node_composite_combine_ycca_cc; static bNodeType ntype; @@ -71,6 +172,7 @@ void register_node_type_cmp_combycca() ntype.declare = file_ns::cmp_node_combycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_combycca); ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc index 4acd2294114..1f0eb04cfc3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -5,60 +5,112 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE YUVA ******************** */ -namespace blender::nodes::node_composite_sepcomb_yuva_cc { +namespace blender::nodes::node_composite_separate_yuva_cc { static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Y")); b.add_output<decl::Float>(N_("U")); b.add_output<decl::Float>(N_("V")); b.add_output<decl::Float>(N_("A")); } -} // namespace blender::nodes::node_composite_sepcomb_yuva_cc +using namespace blender::realtime_compositor; + +class SeparateYUVAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_separate_yuva_itu_709", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateYUVAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_yuva_cc void register_node_type_cmp_sepyuva() { - namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc; + namespace file_ns = blender::nodes::node_composite_separate_yuva_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepyuva_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE YUVA ******************** */ -namespace blender::nodes::node_composite_sepcomb_yuva_cc { +namespace blender::nodes::node_composite_combine_yuva_cc { static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0); + b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f).compositor_domain_priority(1); + b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2); + b.add_input<decl::Float>(N_("A")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes::node_composite_sepcomb_yuva_cc +using namespace blender::realtime_compositor; + +class CombineYUVAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_combine_yuva_itu_709", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineYUVAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_yuva_cc void register_node_type_cmp_combyuva() { - namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc; + namespace file_ns = blender::nodes::node_composite_combine_yuva_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combyuva_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 8aeaafbbf67..9930125aa70 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SET ALPHA ******************** */ @@ -16,8 +20,14 @@ namespace blender::nodes::node_composite_setalpha_cc { static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -33,6 +43,36 @@ static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class SetAlphaShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + if (get_node_set_alpha()->mode == CMP_NODE_SETALPHA_MODE_APPLY) { + GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs); + return; + } + + GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs); + } + + NodeSetAlpha *get_node_set_alpha() + { + return static_cast<NodeSetAlpha *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SetAlphaShaderNode(node); +} + } // namespace blender::nodes::node_composite_setalpha_cc void register_node_type_cmp_setalpha() @@ -47,6 +87,7 @@ void register_node_type_cmp_setalpha() node_type_init(&ntype, file_ns::node_composit_init_setalpha); node_type_storage( &ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc index ab325c4559f..085de69e63e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc @@ -11,6 +11,12 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** SPLIT VIEWER ******************** */ @@ -43,6 +49,70 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C) uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ViewerOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUShader *shader = get_split_viewer_shader(); + GPU_shader_bind(shader); + + const int2 size = compute_domain().size; + + GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio()); + GPU_shader_uniform_2iv(shader, "view_size", size); + + const Result &first_image = get_input("Image"); + first_image.bind_as_texture(shader, "first_image_tx"); + const Result &second_image = get_input("Image_001"); + second_image.bind_as_texture(shader, "second_image_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, size); + + first_image.unbind_as_texture(); + second_image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } + + GPUShader *get_split_viewer_shader() + { + if (get_split_axis() == CMP_NODE_SPLIT_VIEWER_HORIZONTAL) { + return shader_manager().get("compositor_split_viewer_horizontal"); + } + + return shader_manager().get("compositor_split_viewer_vertical"); + } + + CMPNodeSplitViewerAxis get_split_axis() + { + return (CMPNodeSplitViewerAxis)bnode().custom2; + } + + float get_split_ratio() + { + return bnode().custom1 / 100.0f; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ViewerOperation(context, node); +} + } // namespace blender::nodes::node_composite_split_viewer_cc void register_node_type_cmp_splitviewer() @@ -57,6 +127,7 @@ void register_node_type_cmp_splitviewer() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_splitviewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.no_muting = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc index 63d00a0864b..75a96a05863 100644 --- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc +++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc @@ -11,6 +11,8 @@ #include "BKE_context.h" #include "BKE_lib_id.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Stabilize 2D ******************** */ @@ -58,6 +60,23 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class Stabilize2DOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new Stabilize2DOperation(context, node); +} + } // namespace blender::nodes::node_composite_stabilize2d_cc void register_node_type_cmp_stabilize2d() @@ -70,6 +89,7 @@ void register_node_type_cmp_stabilize2d() ntype.declare = file_ns::cmp_node_stabilize2d_declare; ntype.draw_buttons = file_ns::node_composit_buts_stabilize2d; ntype.initfunc_api = file_ns::init; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc index 766f26745ef..4b9264d7e35 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_sunbeams_cc { @@ -38,6 +40,23 @@ static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), P ICON_NONE); } +using namespace blender::realtime_compositor; + +class SunBeamsOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new SunBeamsOperation(context, node); +} + } // namespace blender::nodes::node_composite_sunbeams_cc void register_node_type_cmp_sunbeams() @@ -52,6 +71,7 @@ void register_node_type_cmp_sunbeams() node_type_init(&ntype, file_ns::init); node_type_storage( &ntype, "NodeSunBeams", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.cc b/source/blender/nodes/composite/nodes/node_composite_switch.cc index bda490572e9..767802cc442 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switch.cc +++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Switch ******************** */ @@ -26,6 +28,30 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "check", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class SwitchOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input(get_condition() ? "On" : "Off"); + Result &result = get_result("Image"); + input.pass_through(result); + } + + bool get_condition() + { + return bnode().custom1; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new SwitchOperation(context, node); +} + } // namespace blender::nodes::node_composite_switch_cc void register_node_type_cmp_switch() @@ -38,5 +64,7 @@ void register_node_type_cmp_switch() ntype.declare = file_ns::cmp_node_switch_declare; ntype.draw_buttons = file_ns::node_composit_buts_switch; node_type_size_preset(&ntype, NODE_SIZE_SMALL); + ntype.get_compositor_operation = file_ns::get_compositor_operation; + nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.cc b/source/blender/nodes/composite/nodes/node_composite_switchview.cc index 2cf3da03a05..e74c3b6007a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switchview.cc +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc @@ -11,6 +11,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** SWITCH VIEW ******************** */ @@ -140,6 +142,25 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout, nullptr); } +using namespace blender::realtime_compositor; + +class SwitchViewOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input(context().get_view_name()); + Result &result = get_result("Image"); + input.pass_through(result); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new SwitchViewOperation(context, node); +} + } // namespace blender::nodes::node_composite_switchview_cc void register_node_type_cmp_switch_view() @@ -153,6 +174,7 @@ void register_node_type_cmp_switch_view() ntype.draw_buttons_ex = file_ns::node_composit_buts_switch_view_ex; ntype.initfunc_api = file_ns::init_switch_view; node_type_update(&ntype, file_ns::cmp_node_switch_view_update); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc index 7571e97a2cd..5a628aae7a7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.cc +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** TEXTURE ******************** */ @@ -23,6 +25,24 @@ static void cmp_node_texture_declare(NodeDeclarationBuilder &b) b.add_output<decl::Color>(N_("Color")); } +using namespace blender::realtime_compositor; + +class TextureOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Value").allocate_invalid(); + get_result("Color").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TextureOperation(context, node); +} + } // namespace blender::nodes::node_composite_texture_cc void register_node_type_cmp_texture() @@ -34,6 +54,7 @@ void register_node_type_cmp_texture() cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT); ntype.declare = file_ns::cmp_node_texture_declare; ntype.flag |= NODE_PREVIEW; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc index cdfe97b038d..4cc3d4f32a3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_tonemap_cc { @@ -58,6 +60,23 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po } } +using namespace blender::realtime_compositor; + +class ToneMapOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ToneMapOperation(context, node); +} + } // namespace blender::nodes::node_composite_tonemap_cc void register_node_type_cmp_tonemap() @@ -71,6 +90,7 @@ void register_node_type_cmp_tonemap() ntype.draw_buttons = file_ns::node_composit_buts_tonemap; node_type_init(&ntype, file_ns::node_composit_init_tonemap); node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc index 0e99ff59327..0e9bd800f44 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -18,6 +18,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_trackpos_cc { @@ -102,6 +104,25 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN } } +using namespace blender::realtime_compositor; + +class TrackPositionOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("X").allocate_invalid(); + get_result("Y").allocate_invalid(); + get_result("Speed").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TrackPositionOperation(context, node); +} + } // namespace blender::nodes::node_composite_trackpos_cc void register_node_type_cmp_trackpos() @@ -116,6 +137,7 @@ void register_node_type_cmp_trackpos() ntype.initfunc_api = file_ns::init; node_type_storage( &ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.cc b/source/blender/nodes/composite/nodes/node_composite_transform.cc index fe72f5e33ca..7c5866d2d06 100644 --- a/source/blender/nodes/composite/nodes/node_composite_transform.cc +++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc @@ -5,9 +5,15 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" +#include "BLI_float3x3.hh" +#include "BLI_math_vector.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Transform ******************** */ @@ -16,15 +22,30 @@ namespace blender::nodes::node_composite_transform_cc { static void cmp_node_transform_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("X")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Y")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_expects_single_value(); b.add_input<decl::Float>(N_("Angle")) .default_value(0.0f) .min(-10000.0f) .max(10000.0f) - .subtype(PROP_ANGLE); - b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX); + .subtype(PROP_ANGLE) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Scale")) + .default_value(1.0f) + .min(0.0001f) + .max(CMP_SCALE_MAX) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } @@ -33,6 +54,51 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class TransformOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input("Image"); + Result &result = get_result("Image"); + input.pass_through(result); + + const float2 translation = float2(get_input("X").get_float_value_default(0.0f), + get_input("Y").get_float_value_default(0.0f)); + const float rotation = get_input("Angle").get_float_value_default(0.0f); + const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f)); + + const float3x3 transformation = float3x3::from_translation_rotation_scale( + translation, rotation, scale); + + result.transform(transformation); + result.get_realization_options().interpolation = get_interpolation(); + } + + Interpolation get_interpolation() + { + switch (bnode().custom1) { + case 0: + return Interpolation::Nearest; + case 1: + return Interpolation::Bilinear; + case 2: + return Interpolation::Bicubic; + } + + BLI_assert_unreachable(); + return Interpolation::Nearest; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TransformOperation(context, node); +} + } // namespace blender::nodes::node_composite_transform_cc void register_node_type_cmp_transform() @@ -44,6 +110,7 @@ void register_node_type_cmp_transform() cmp_node_type_base(&ntype, CMP_NODE_TRANSFORM, "Transform", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_transform_declare; ntype.draw_buttons = file_ns::node_composit_buts_transform; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc index bbdc8ca4d31..fbd53b8310f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_translate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc @@ -5,9 +5,14 @@ * \ingroup cmpnodes */ +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -16,9 +21,19 @@ namespace blender::nodes::node_composite_translate_cc { static void cmp_node_translate_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("X")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Y")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } @@ -34,6 +49,59 @@ static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "wrap_axis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class TranslateOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input("Image"); + Result &result = get_result("Image"); + input.pass_through(result); + + float x = get_input("X").get_float_value_default(0.0f); + float y = get_input("Y").get_float_value_default(0.0f); + if (get_use_relative()) { + x *= input.domain().size.x; + y *= input.domain().size.y; + } + + const float2 translation = float2(x, y); + const float3x3 transformation = float3x3::from_translation(translation); + + result.transform(transformation); + result.get_realization_options().repeat_x = get_repeat_x(); + result.get_realization_options().repeat_y = get_repeat_y(); + } + + NodeTranslateData &get_node_translate() + { + return *static_cast<NodeTranslateData *>(bnode().storage); + } + + bool get_use_relative() + { + return get_node_translate().relative; + } + + bool get_repeat_x() + { + return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY); + } + + bool get_repeat_y() + { + return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TranslateOperation(context, node); +} + } // namespace blender::nodes::node_composite_translate_cc void register_node_type_cmp_translate() @@ -48,6 +116,7 @@ void register_node_type_cmp_translate() node_type_init(&ntype, file_ns::node_composit_init_translate); node_type_storage( &ntype, "NodeTranslateData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc index df669d5beda..03a7bc61924 100644 --- a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc @@ -5,18 +5,33 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" + +#include "IMB_colormanagement.h" + +#include "BKE_colorband.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" #include "BKE_colorband.h" /* **************** VALTORGB ******************** */ -namespace blender::nodes::node_composite_val_to_rgb_cc { +namespace blender::nodes::node_composite_color_ramp_cc { static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Color>(N_("Image")); + b.add_input<decl::Float>(N_("Fac")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_output<decl::Color>(N_("Image")).compositor_domain_priority(0); b.add_output<decl::Float>(N_("Alpha")); } @@ -25,11 +40,94 @@ static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node) node->storage = BKE_colorband_add(true); } -} // namespace blender::nodes::node_composite_val_to_rgb_cc +using namespace blender::realtime_compositor; + +class ColorRampShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + struct ColorBand *color_band = get_color_band(); + + /* Common / easy case optimization. */ + if ((color_band->tot <= 2) && (color_band->color_mode == COLBAND_BLEND_RGB)) { + float mul_bias[2]; + switch (color_band->ipotype) { + case COLBAND_INTERP_LINEAR: + mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos); + mul_bias[1] = -mul_bias[0] * color_band->data[0].pos; + GPU_stack_link(material, + &bnode(), + "valtorgb_opti_linear", + inputs, + outputs, + GPU_uniform(mul_bias), + GPU_uniform(&color_band->data[0].r), + GPU_uniform(&color_band->data[1].r)); + return; + case COLBAND_INTERP_CONSTANT: + mul_bias[1] = max_ff(color_band->data[0].pos, color_band->data[1].pos); + GPU_stack_link(material, + &bnode(), + "valtorgb_opti_constant", + inputs, + outputs, + GPU_uniform(&mul_bias[1]), + GPU_uniform(&color_band->data[0].r), + GPU_uniform(&color_band->data[1].r)); + return; + case COLBAND_INTERP_EASE: + mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos); + mul_bias[1] = -mul_bias[0] * color_band->data[0].pos; + GPU_stack_link(material, + &bnode(), + "valtorgb_opti_ease", + inputs, + outputs, + GPU_uniform(mul_bias), + GPU_uniform(&color_band->data[0].r), + GPU_uniform(&color_band->data[1].r)); + return; + default: + BLI_assert_unreachable(); + return; + } + } + + float *array, layer; + int size; + BKE_colorband_evaluate_table_rgba(color_band, &array, &size); + GPUNodeLink *tex = GPU_color_band(material, size, array, &layer); + + if (color_band->ipotype == COLBAND_INTERP_CONSTANT) { + GPU_stack_link( + material, &bnode(), "valtorgb_nearest", inputs, outputs, tex, GPU_constant(&layer)); + return; + } + + GPU_stack_link(material, &bnode(), "valtorgb", inputs, outputs, tex, GPU_constant(&layer)); + } + + struct ColorBand *get_color_band() + { + return static_cast<struct ColorBand *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorRampShaderNode(node); +} + +} // namespace blender::nodes::node_composite_color_ramp_cc void register_node_type_cmp_valtorgb() { - namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc; + namespace file_ns = blender::nodes::node_composite_color_ramp_cc; static bNodeType ntype; @@ -38,31 +136,63 @@ void register_node_type_cmp_valtorgb() node_type_size(&ntype, 240, 200, 320); node_type_init(&ntype, file_ns::node_composit_init_valtorgb); node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** RGBTOBW ******************** */ -namespace blender::nodes::node_composite_val_to_rgb_cc { +namespace blender::nodes::node_composite_rgb_to_bw_cc { static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Val")); } -} // namespace blender::nodes::node_composite_val_to_rgb_cc +using namespace blender::realtime_compositor; + +class RGBToBWShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + + GPU_stack_link(material, + &bnode(), + "color_to_luminance", + inputs, + outputs, + GPU_constant(luminance_coefficients)); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new RGBToBWShaderNode(node); +} + +} // namespace blender::nodes::node_composite_rgb_to_bw_cc void register_node_type_cmp_rgbtobw() { - namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc; + namespace file_ns = blender::nodes::node_composite_rgb_to_bw_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_rgbtobw_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc index a3269d3d1c2..a96e1db14ad 100644 --- a/source/blender/nodes/composite/nodes/node_composite_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_value.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** VALUE ******************** */ @@ -16,6 +18,29 @@ static void cmp_node_value_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Value")).default_value(0.5f); } +using namespace blender::realtime_compositor; + +class ValueOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &result = get_result("Value"); + result.allocate_single_value(); + + const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first); + float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value; + + result.set_float_value(value); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ValueOperation(context, node); +} + } // namespace blender::nodes::node_composite_value_cc void register_node_type_cmp_value() @@ -27,6 +52,7 @@ void register_node_type_cmp_value() cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT); ntype.declare = file_ns::cmp_node_value_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc index 741f2e0e816..515478da75d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** VECTOR BLUR ******************** */ @@ -51,6 +53,23 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "use_curved", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class VectorBlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new VectorBlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_vec_blur_cc void register_node_type_cmp_vecblur() @@ -65,6 +84,7 @@ void register_node_type_cmp_vecblur() node_type_init(&ntype, file_ns::node_composit_init_vecblur); node_type_storage( &ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index 05f395183b5..4e82b31ca47 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + #include "BKE_global.h" #include "BKE_image.h" @@ -13,6 +15,13 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** VIEWER ******************** */ @@ -55,6 +64,125 @@ static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), } } +using namespace blender::realtime_compositor; + +class ViewerOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + if (image.is_single_value() && alpha.is_single_value()) { + execute_clear(); + } + else if (ignore_alpha()) { + execute_ignore_alpha(); + } + else if (!node().input_by_identifier("Alpha")->is_logically_linked()) { + execute_copy(); + } + else { + execute_set_alpha(); + } + } + + /* Executes when all inputs are single values, in which case, the output texture can just be + * cleared to the appropriate color. */ + void execute_clear() + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + float4 color = image.get_color_value(); + if (ignore_alpha()) { + color.w = 1.0f; + } + else if (node().input_by_identifier("Alpha")->is_logically_linked()) { + color.w = alpha.get_float_value(); + } + + GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color); + } + + /* Executes when the alpha channel of the image is ignored. */ + void execute_ignore_alpha() + { + GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "input_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* Executes when the image texture is written with no adjustments and can thus be copied directly + * to the output texture. */ + void execute_copy() + { + const Result &image = get_input("Image"); + + /* Make sure any prior writes to the texture are reflected before copying it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + GPU_texture_copy(context().get_output_texture(), image.texture()); + } + + /* Executes when the alpha channel of the image is set as the value of the input alpha. */ + void execute_set_alpha() + { + GPUShader *shader = shader_manager().get("compositor_set_alpha"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "image_tx"); + + const Result &alpha = get_input("Alpha"); + alpha.bind_as_texture(shader, "alpha_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + alpha.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the + * alpha channel of the image is retained, but only if the alpha input is not linked. If the + * alpha input is linked, it the value of that input will be used as the alpha of the image. */ + bool ignore_alpha() + { + return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA; + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ViewerOperation(context, node); +} + } // namespace blender::nodes::node_composite_viewer_cc void register_node_type_cmp_viewer() @@ -70,6 +198,7 @@ void register_node_type_cmp_viewer() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_viewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.no_muting = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc index be90aeb7acc..e5f460099e9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc +++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Z COMBINE ******************** */ @@ -33,6 +35,24 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "use_antialias_z", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ZCombineOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Z").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ZCombineOperation(context, node); +} + } // namespace blender::nodes::node_composite_zcombine_cc void register_node_type_cmp_zcombine() @@ -44,6 +64,7 @@ void register_node_type_cmp_zcombine() cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_zcombine_declare; ntype.draw_buttons = file_ns::node_composit_buts_zcombine; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index ddd8c8949b1..31c00cc6b82 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -76,8 +76,8 @@ set(SRC nodes/node_geo_input_index.cc nodes/node_geo_input_instance_rotation.cc nodes/node_geo_input_instance_scale.cc - nodes/node_geo_input_material_index.cc nodes/node_geo_input_material.cc + nodes/node_geo_input_material_index.cc nodes/node_geo_input_mesh_edge_angle.cc nodes/node_geo_input_mesh_edge_neighbors.cc nodes/node_geo_input_mesh_edge_vertices.cc @@ -118,9 +118,9 @@ set(SRC nodes/node_geo_mesh_to_points.cc nodes/node_geo_mesh_to_volume.cc nodes/node_geo_object_info.cc + nodes/node_geo_points.cc nodes/node_geo_points_to_vertices.cc nodes/node_geo_points_to_volume.cc - nodes/node_geo_points.cc nodes/node_geo_proximity.cc nodes/node_geo_raycast.cc nodes/node_geo_realize_instances.cc @@ -134,8 +134,8 @@ set(SRC nodes/node_geo_set_curve_radius.cc nodes/node_geo_set_curve_tilt.cc nodes/node_geo_set_id.cc - nodes/node_geo_set_material_index.cc nodes/node_geo_set_material.cc + nodes/node_geo_set_material_index.cc nodes/node_geo_set_point_radius.cc nodes/node_geo_set_position.cc nodes/node_geo_set_shade_smooth.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index cf29c752257..a6c67cac916 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -221,7 +221,7 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + - v2_density_factor * bary_coord.z; + v2_density_factor * bary_coord.z; const float hash = noise::hash_float_to_float(bary_coord); if (hash > probability) { diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc index 12707623049..478a6812c36 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc @@ -32,7 +32,7 @@ static const char *gpu_shader_get_name(int mode) case MA_RAMP_SCREEN: return "mix_screen"; case MA_RAMP_DIV: - return "mix_div"; + return "mix_div_fallback"; case MA_RAMP_DIFF: return "mix_diff"; case MA_RAMP_DARK: @@ -70,18 +70,23 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat, { const char *name = gpu_shader_get_name(node->custom1); - if (name != nullptr) { - int ret = GPU_stack_link(mat, node, name, in, out); - if (ret && node->custom2 & SHD_MIXRGB_CLAMP) { - const float min[3] = {0.0f, 0.0f, 0.0f}; - const float max[3] = {1.0f, 1.0f, 1.0f}; - GPU_link( - mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link); - } - return ret; + if (name == nullptr) { + return 0; } - return 0; + const float min = 0.0f; + const float max = 1.0f; + const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec); + GPU_link(mat, "clamp_value", factor_link, GPU_constant(&min), GPU_constant(&max), &in[0].link); + + int ret = GPU_stack_link(mat, node, name, in, out); + + if (ret && node->custom2 & SHD_MIXRGB_CLAMP) { + const float min[3] = {0.0f, 0.0f, 0.0f}; + const float max[3] = {1.0f, 1.0f, 1.0f}; + GPU_link(mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link); + } + return ret; } class MixRGBFunction : public fn::MultiFunction { diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_rgb.cc index c854bc733a3..3d28f5278a2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_rgb.cc @@ -17,7 +17,7 @@ static void node_declare(NodeDeclarationBuilder &b) static int gpu_shader_rgb(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, + GPUNodeStack * /*in*/, GPUNodeStack *out) { const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first); diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index b6b7fe10cf9..14dbb3b25eb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -17,7 +17,7 @@ static void sh_node_value_declare(NodeDeclarationBuilder &b) static int gpu_shader_value(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, + GPUNodeStack * /*in*/, GPUNodeStack *out) { const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first); diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 71138134370..9d2516969cf 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -174,8 +174,8 @@ if(WITH_CODEC_SNDFILE) add_definitions(-DWITH_SNDFILE) endif() -if(WITH_COMPOSITOR) - add_definitions(-DWITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_CYCLES) diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c index fe5111c37f2..a744f3fd4fa 100644 --- a/source/blender/python/intern/bpy_app_build_options.c +++ b/source/blender/python/intern/bpy_app_build_options.c @@ -18,7 +18,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = { {"codec_avi", NULL}, {"codec_ffmpeg", NULL}, {"codec_sndfile", NULL}, - {"compositor", NULL}, + {"compositor_cpu", NULL}, {"cycles", NULL}, {"cycles_osl", NULL}, {"freestyle", NULL}, @@ -104,7 +104,7 @@ static PyObject *make_builtopts_info(void) SetObjIncref(Py_False); #endif -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU SetObjIncref(Py_True); #else SetObjIncref(Py_False); diff --git a/source/blender/python/intern/bpy_interface_atexit.c b/source/blender/python/intern/bpy_interface_atexit.c index 1ba3ab6a40b..cba9bd59abf 100644 --- a/source/blender/python/intern/bpy_interface_atexit.c +++ b/source/blender/python/intern/bpy_interface_atexit.c @@ -32,7 +32,7 @@ static PyObject *func_bpy_atregister = NULL; /* borrowed reference, `atexit` hol static void atexit_func_call(const char *func_name, PyObject *atexit_func_arg) { - /* NOTE(campbell): no error checking, if any of these fail we'll get a crash + /* NOTE(@campbellbarton): no error checking, if any of these fail we'll get a crash * this is intended, but if its problematic it could be changed. */ PyObject *atexit_mod = PyImport_ImportModuleLevel("atexit", NULL, NULL, NULL, 0); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index d9c004fb6fa..179a0250688 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -780,7 +780,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) return ret; } -/* NOTE(campbell): Regarding comparison `__cmp__`: +/* NOTE(@campbellbarton): Regarding comparison `__cmp__`: * checking the 'ptr->data' matches works in almost all cases, * however there are a few RNA properties that are fake sub-structs and * share the pointer with the parent, in those cases this happens 'a.b == a' @@ -8254,7 +8254,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, continue; } - /* TODO(campbell): this is used for classmethod's too, + /* TODO(@campbellbarton): this is used for classmethod's too, * even though class methods should have 'FUNC_USE_SELF_TYPE' set, see Operator.poll for eg. * Keep this as-is since it's working, but we should be using * 'FUNC_USE_SELF_TYPE' for many functions. */ @@ -8345,7 +8345,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, continue; } - /* TODO(campbell): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */ + /* TODO(@campbellbarton): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */ identifier = RNA_property_identifier(prop); item = PyObject_GetAttrString(py_class, identifier); diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 9ffe2879779..54497a6572f 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -760,8 +760,8 @@ void RE_bake_pixels_populate(Mesh *me, for (int a = 0; a < 3; a++) { const float *uv = mloopuv[lt->tri[a]].uv; - /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our - * intersection tests where a pixel gets in between 2 faces or the middle of a quad, + /* NOTE(@campbellbarton): workaround for pixel aligned UVs which are common and can screw + * up our intersection tests where a pixel gets in between 2 faces or the middle of a quad, * camera aligned quads also have this problem but they are less common. * Add a small offset to the UVs, fixes bug T18685. */ vec[a][0] = (uv[0] - bk_image->uv_offset[0]) * (float)bk_image->width - (0.5f + 0.001f); diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc index 37ef9213615..92146155437 100644 --- a/source/blender/render/intern/texture_margin.cc +++ b/source/blender/render/intern/texture_margin.cc @@ -558,8 +558,8 @@ static void generate_margin(ImBuf *ibuf, for (int a = 0; a < 3; a++) { const float *uv = mloopuv[lt->tri[a]].uv; - /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our - * intersection tests where a pixel gets in between 2 faces or the middle of a quad, + /* NOTE(@campbellbarton): workaround for pixel aligned UVs which are common and can screw up + * our intersection tests where a pixel gets in between 2 faces or the middle of a quad, * camera aligned quads also have this problem but they are less common. * Add a small offset to the UVs, fixes bug T18685. */ vec[a][0] = (uv[0] - uv_offset[0]) * (float)ibuf->x - (0.5f + 0.001f); diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 6cbd21d0abe..c434638549c 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -165,11 +165,11 @@ if(WIN32 OR APPLE) endif() endif() -if(WITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) list(APPEND LIB bf_compositor ) - add_definitions(-DWITH_COMPOSITOR) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_XR_OPENXR) diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index 4ecadbc5685..e165cb6b4f8 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -1053,7 +1053,7 @@ void WM_gizmomaptype_group_unlink(bContext *C, WM_gizmomaptype_group_free(gzgt_ref); } - /* TODO(campbell): Gizmos may share key-maps, for now don't + /* TODO(@campbellbarton): Gizmos may share key-maps, for now don't * remove however we could flag them as temporary/owned by the gizmo. */ #if 0 /* NOTE: we may want to keep this key-map for editing. */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index f5974a2176b..9903b0e50fd 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -259,7 +259,7 @@ bool WM_gizmomap_minmax(const wmGizmoMap *gzmap, * \param poll: Polling function for excluding gizmos. * \param data: Custom data passed to \a poll * - * TODO(campbell): this uses unreliable order, + * TODO(@campbellbarton): this uses unreliable order, * best we use an iterator function instead of a hash. */ static GHash *WM_gizmomap_gizmo_hash_new(const bContext *C, @@ -430,9 +430,9 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas return; } - /* TODO(campbell): This will need it own shader probably? + /* TODO(@campbellbarton): This will need it own shader probably? * Don't think it can be handled from that point though. */ - /* const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; */ + // const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; bool is_depth_prev = false; @@ -501,7 +501,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C, bool *r_use_select_bias) { - /* TODO(campbell): this depends on depth buffer being written to, + /* TODO(@campbellbarton): this depends on depth buffer being written to, * currently broken for the 3D view. */ bool is_depth_prev = false; bool is_depth_skip_prev = false; @@ -674,7 +674,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, * This way we always use the first hit. */ if (has_3d) { - /* The depth buffer is needed for for gizmos to obscure each other. */ + /* The depth buffer is needed for gizmos to obscure each other. */ GPUViewport *viewport = WM_draw_region_get_viewport(CTX_wm_region(C)); /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index d90259c0cde..77f6b3c861f 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -5214,6 +5214,13 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.prev_type = event.type; event.prev_val = event.val; + /* Always use modifiers from the active window since + changes to modifiers aren't sent to inactive windows, see: T66088. */ + if ((wm->winactive != win) && (wm->winactive && wm->winactive->eventstate)) { + event.modifier = wm->winactive->eventstate->modifier; + event.keymodifier = wm->winactive->eventstate->keymodifier; + } + /* Ensure the event state is correct, any deviation from this may cause bugs. * * NOTE: #EVENT_NONE is set when unknown keys are pressed, @@ -5256,6 +5263,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void if (win_other) { wmEvent event_other = *win_other->eventstate; + /* Use the modifier state of this window. */ + event_other.modifier = event.modifier; + event_other.keymodifier = event.keymodifier; + /* See comment for this operation on `event` for details. */ event_other.prev_type = event_other.type; event_other.prev_val = event_other.val; @@ -5345,6 +5356,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void if (win_other) { wmEvent event_other = *win_other->eventstate; + /* Use the modifier state of this window. */ + event_other.modifier = event.modifier; + event_other.keymodifier = event.keymodifier; + /* See comment for this operation on `event` for details. */ event_other.prev_type = event_other.type; event_other.prev_val = event_other.val; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 45e8f8786df..1819ed13be3 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1887,9 +1887,6 @@ static void wm_autosave_location(char *filepath) { const int pid = abs(getpid()); char path[1024]; -#ifdef WIN32 - const char *savedir; -#endif /* Normally there is no need to check for this to be NULL, * however this runs on exit when it may be cleared. */ @@ -1915,7 +1912,7 @@ static void wm_autosave_location(char *filepath) * through BLI_windows_get_default_root_dir(). * If there is no C:\tmp autosave fails. */ if (!BLI_exists(BKE_tempdir_base())) { - savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL); + const char *savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL); BLI_make_file_string("/", filepath, savedir, path); return; } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 7f5ec77e16d..624e434e784 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -540,7 +540,7 @@ void WM_exit_ex(bContext *C, const bool do_python) BKE_vfont_clipboard_free(); BKE_node_clipboard_free(); -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU COM_deinitialize(); #endif diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 99f117f267a..790019b68b8 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -1807,21 +1807,22 @@ static char *wm_main_playanim_intern(int argc, const char **argv) AUD_Sound_free(source); source = NULL; #endif + /* we still miss freeing a lot!, * but many areas could skip initialization too for anim play */ - GPU_shader_free_builtin_shaders(); + IMB_exit(); + DEG_free_node_types(); + + BLF_exit(); if (g_WS.gpu_context) { GPU_context_active_set(g_WS.gpu_context); + GPU_exit(); GPU_context_discard(g_WS.gpu_context); g_WS.gpu_context = NULL; } - BLF_exit(); - - GPU_exit(); - GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window); /* early exit, IMB and BKE should be exited only in end */ @@ -1830,9 +1831,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv) return filepath; } - IMB_exit(); - DEG_free_node_types(); - totblock = MEM_get_memory_blocks_in_use(); if (totblock != 0) { /* prints many bAKey, bArgument's which are tricky to fix */ diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index a5690b52a5a..0c31ff87fdd 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1113,14 +1113,22 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt } wmWindow *win = GHOST_GetWindowUserData(ghostwin); + /* Win23/GHOST modifier bug, see T40317 */ +#ifndef WIN32 +//# define USE_WIN_ACTIVATE +#endif + switch (type) { case GHOST_kEventWindowDeactivate: wm_event_add_ghostevent(wm, win, type, data); win->active = 0; /* XXX */ - /* clear modifiers for inactive windows */ + /* When window activation is enabled, these modifiers are set with window activation. + * Otherwise leave them set so re-activation doesn't loose keys which are held. */ +#ifdef USE_WIN_ACTIVATE win->eventstate->modifier = 0; win->eventstate->keymodifier = 0; +#endif break; case GHOST_kEventWindowActivate: { @@ -1128,11 +1136,6 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt (query_qual(CONTROL) ? KM_CTRL : 0) | (query_qual(ALT) ? KM_ALT : 0) | (query_qual(OS) ? KM_OSKEY : 0)); - /* Win23/GHOST modifier bug, see T40317 */ -#ifndef WIN32 -//# define USE_WIN_ACTIVATE -#endif - /* No context change! C->wm->windrawable is drawable, or for area queues. */ wm->winactive = win; |