diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h index 433c25084ad..3abaf5256df 100644 --- a/source/blender/blenkernel/BKE_cryptomatte.h +++ b/source/blender/blenkernel/BKE_cryptomatte.h @@ -24,19 +24,30 @@ #pragma once #include "BLI_sys_types.h" +#include "DNA_layer_types.h" #ifdef __cplusplus extern "C" { #endif -struct Main; +struct CryptomatteSession; struct Material; +struct ID; +struct Main; struct Object; +struct RenderResult; + +struct CryptomatteSession *BKE_cryptomatte_init(void); +void BKE_cryptomatte_finish(struct CryptomatteSession *session); +void BKE_cryptomatte_free(struct CryptomatteSession *session); uint32_t BKE_cryptomatte_hash(const char *name, int name_len); -uint32_t BKE_cryptomatte_object_hash(const struct Object *object); -uint32_t BKE_cryptomatte_material_hash(const struct Material *material); -uint32_t BKE_cryptomatte_asset_hash(const struct Object *object); +uint32_t BKE_cryptomatte_object_hash(struct CryptomatteSession *session, + const struct Object *object); +uint32_t BKE_cryptomatte_material_hash(struct CryptomatteSession *session, + const struct Material *material); +uint32_t BKE_cryptomatte_asset_hash(struct CryptomatteSession *session, + const struct Object *object); float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash); char *BKE_cryptomatte_entries_to_matte_id(struct NodeCryptomatte *node_storage); @@ -44,6 +55,12 @@ void BKE_cryptomatte_matte_id_to_entries(const struct Main *bmain, struct NodeCryptomatte *node_storage, const char *matte_id); +void BKE_cryptomatte_store_metadata(struct CryptomatteSession *session, + struct RenderResult *render_result, + const ViewLayer *view_layer, + eViewLayerCryptomatteFlags cryptomatte_layer, + const char *cryptomatte_layer_name); + #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc index 4bbeb088628..3ed66960162 100644 --- a/source/blender/blenkernel/intern/cryptomatte.cc +++ b/source/blender/blenkernel/intern/cryptomatte.cc @@ -22,8 +22,10 @@ */ #include "BKE_cryptomatte.h" +#include "BKE_image.h" #include "BKE_main.h" +#include "DNA_layer_types.h" #include "DNA_material_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" @@ -32,48 +34,142 @@ #include "BLI_dynstr.h" #include "BLI_hash_mm3.h" #include "BLI_listbase.h" +#include "BLI_set.hh" #include "BLI_string.h" #include "MEM_guardedalloc.h" #include +#include #include #include -static uint32_t cryptomatte_hash(const ID *id) +enum CryptomatteLayerState { + EMPTY, + FILLED, + CLOSED, +}; + +struct CryptomatteLayer { + CryptomatteLayerState state = CryptomatteLayerState::EMPTY; + blender::Set names; + std::stringstream manifest; + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("cryptomatte:CryptomatteLayer") +#endif + + void add_hash(std::string name, uint32_t cryptomatte_hash) + { + BLI_assert(state != CryptomatteLayerState::CLOSED); + const bool first_item = names.is_empty(); + if (!names.add(name)) { + return; + } + + if (first_item) { + state = CryptomatteLayerState::FILLED; + manifest << "{"; + } + else { + manifest << ","; + } + manifest << quoted(name) << ":\""; + manifest << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex + << cryptomatte_hash; + manifest << "\""; + } + + void close_manifest() + { + BLI_assert(state != CryptomatteLayerState::CLOSED); + if (state == CryptomatteLayerState::FILLED) { + manifest << "}"; + } + state = CryptomatteLayerState::CLOSED; + } + + std::string manifest_get_string() + { + BLI_assert(state == CryptomatteLayerState::CLOSED); + return manifest.str(); + } +}; + +struct CryptomatteSession { + CryptomatteLayer objects; + CryptomatteLayer assets; + CryptomatteLayer materials; + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("cryptomatte:CryptomatteSession") +#endif + + void finish() + { + objects.close_manifest(); + materials.close_manifest(); + assets.close_manifest(); + } +}; + +CryptomatteSession *BKE_cryptomatte_init(void) { - const char *name = &id->name[2]; - const int name_len = BLI_strnlen(name, MAX_NAME); - uint32_t cryptohash_int = BKE_cryptomatte_hash(name, name_len); - return cryptohash_int; + CryptomatteSession *session = new CryptomatteSession(); + return session; } -uint32_t BKE_cryptomatte_hash(const char *name, int name_len) +void BKE_cryptomatte_finish(CryptomatteSession *session) +{ + BLI_assert(session != NULL); + session->finish(); +} + +void BKE_cryptomatte_free(CryptomatteSession *session) +{ + BLI_assert(session != NULL); + delete session; +} + +uint32_t BKE_cryptomatte_hash(const char *name, const int name_len) { uint32_t cryptohash_int = BLI_hash_mm3((const unsigned char *)name, name_len, 0); return cryptohash_int; } -uint32_t BKE_cryptomatte_object_hash(const Object *object) +static uint32_t cryptomatte_hash(CryptomatteLayer *layer, const ID *id) { - return cryptomatte_hash(&object->id); + const char *name = &id->name[2]; + const int name_len = BLI_strnlen(name, MAX_NAME - 2); + uint32_t cryptohash_int = BKE_cryptomatte_hash(name, name_len); + + if (layer != nullptr) { + layer->add_hash(std::string(name, name_len), cryptohash_int); + } + + return cryptohash_int; } -uint32_t BKE_cryptomatte_material_hash(const Material *material) +uint32_t BKE_cryptomatte_object_hash(CryptomatteSession *session, const Object *object) { - if (material == nullptr) { + return cryptomatte_hash(&session->objects, &object->id); +} + +uint32_t BKE_cryptomatte_material_hash(CryptomatteSession *session, const Material *material) +{ + if (material == NULL) { return 0.0f; } - return cryptomatte_hash(&material->id); + return cryptomatte_hash(&session->materials, &material->id); } -uint32_t BKE_cryptomatte_asset_hash(const Object *object) +uint32_t BKE_cryptomatte_asset_hash(CryptomatteSession *session, const Object *object) { const Object *asset_object = object; - while (asset_object->parent != nullptr) { + while (asset_object->parent != NULL) { asset_object = asset_object->parent; } - return cryptomatte_hash(&asset_object->id); + return cryptomatte_hash(&session->assets, &asset_object->id); } /* Convert a cryptomatte hash to a float. @@ -187,4 +283,64 @@ void BKE_cryptomatte_matte_id_to_entries(const Main *bmain, BLI_addtail(&node_storage->entries, entry); } } -} \ No newline at end of file +} + +static std::string cryptomatte_determine_name(const ViewLayer *view_layer, + const std::string cryptomatte_layer_name) +{ + std::stringstream stream; + const size_t view_layer_name_len = BLI_strnlen(view_layer->name, sizeof(view_layer->name)); + stream << std::string(view_layer->name, view_layer_name_len) << "." << cryptomatte_layer_name; + return stream.str(); +} + +static uint32_t cryptomatte_determine_identifier(const std::string name) +{ + return BLI_hash_mm3(reinterpret_cast(name.c_str()), name.length(), 0); +} + +static std::string cryptomatte_determine_prefix(const std::string name) +{ + std::stringstream stream; + const uint32_t render_pass_identifier = cryptomatte_determine_identifier(name); + stream << "cryptomatte/"; + stream << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex + << render_pass_identifier; + stream << "/"; + return stream.str(); +} + +void BKE_cryptomatte_store_metadata(struct CryptomatteSession *session, + RenderResult *render_result, + const ViewLayer *view_layer, + eViewLayerCryptomatteFlags cryptomatte_layer, + const char *cryptomatte_layer_name) +{ + /* Create Manifest. */ + CryptomatteLayer *layer = nullptr; + switch (cryptomatte_layer) { + case VIEW_LAYER_CRYPTOMATTE_OBJECT: + layer = &session->objects; + break; + case VIEW_LAYER_CRYPTOMATTE_MATERIAL: + layer = &session->materials; + break; + case VIEW_LAYER_CRYPTOMATTE_ASSET: + layer = &session->assets; + break; + default: + BLI_assert(!"Incorrect cryptomatte layer"); + break; + } + + const std::string manifest = layer->manifest_get_string(); + const std::string name = cryptomatte_determine_name(view_layer, cryptomatte_layer_name); + const std::string prefix = cryptomatte_determine_prefix(name); + + /* Store the meta data into the render result. */ + BKE_render_result_stamp_data(render_result, (prefix + "name").c_str(), name.c_str()); + BKE_render_result_stamp_data(render_result, (prefix + "hash").c_str(), "MurmurHash3_32"); + BKE_render_result_stamp_data( + render_result, (prefix + "conversion").c_str(), "uint32_to_float32"); + BKE_render_result_stamp_data(render_result, (prefix + "manifest").c_str(), manifest.c_str()); +} diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index a152f90e2c5..c120df7e897 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -260,6 +260,7 @@ DrawEngineType draw_engine_basic_type = { NULL, NULL, NULL, + NULL, }; #undef BASIC_ENGINE diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 9150de7184a..ce714715c6c 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -125,6 +125,7 @@ void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata) return; } if (eevee_cryptomatte_active_layers(view_layer) != 0) { + g_data->cryptomatte_session = BKE_cryptomatte_init(); g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT; g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0; @@ -193,24 +194,26 @@ static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedat const ViewLayer *view_layer = draw_ctx->view_layer; const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers( view_layer); + EEVEE_PrivateData *g_data = vedata->stl->g_data; float cryptohash[4] = {0.0f}; EEVEE_PassList *psl = vedata->psl; int layer_offset = 0; if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { - uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash(ob); + uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash(g_data->cryptomatte_session, ob); float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); cryptohash[layer_offset] = cryptomatte_color_value; layer_offset++; } if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { - uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash(material); + uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash(g_data->cryptomatte_session, + material); float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); cryptohash[layer_offset] = cryptomatte_color_value; layer_offset++; } if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { - uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash(ob); + uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash(g_data->cryptomatte_session, ob); float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); cryptohash[layer_offset] = cryptomatte_color_value; layer_offset++; @@ -310,6 +313,12 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s } } +void EEVEE_cryptomatte_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) +{ + EEVEE_PrivateData *g_data = vedata->stl->g_data; + BKE_cryptomatte_finish(g_data->cryptomatte_session); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -677,6 +686,37 @@ void EEVEE_cryptomatte_render_result(RenderLayer *rl, } } +void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, RenderResult *render_result) +{ + EEVEE_PrivateData *g_data = vedata->stl->g_data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const ViewLayer *view_layer = draw_ctx->view_layer; + const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag & + VIEW_LAYER_CRYPTOMATTE_ALL; + BLI_assert(g_data->cryptomatte_session); + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { + BKE_cryptomatte_store_metadata(g_data->cryptomatte_session, + render_result, + view_layer, + VIEW_LAYER_CRYPTOMATTE_OBJECT, + "CryptoObject"); + } + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { + BKE_cryptomatte_store_metadata(g_data->cryptomatte_session, + render_result, + view_layer, + VIEW_LAYER_CRYPTOMATTE_MATERIAL, + "CryptoMaterial"); + } + if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { + BKE_cryptomatte_store_metadata(g_data->cryptomatte_session, + render_result, + view_layer, + VIEW_LAYER_CRYPTOMATTE_ASSET, + "CryptoAsset"); + } +} + /** \} */ void EEVEE_cryptomatte_free(EEVEE_Data *vedata) @@ -684,4 +724,8 @@ void EEVEE_cryptomatte_free(EEVEE_Data *vedata) EEVEE_PrivateData *g_data = vedata->stl->g_data; MEM_SAFE_FREE(g_data->cryptomatte_accum_buffer); MEM_SAFE_FREE(g_data->cryptomatte_download_buffer); -} + if (g_data->cryptomatte_session) { + BKE_cryptomatte_free(g_data->cryptomatte_session); + g_data->cryptomatte_session = NULL; + } +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index f233b0fda96..c6760de5cfa 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -570,8 +570,6 @@ static void eevee_render_to_image(void *vedata, EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur); if (RE_engine_test_break(engine)) { - /* Cryptomatte buffers are freed during render_read_result */ - EEVEE_cryptomatte_free(vedata); return; } @@ -586,6 +584,16 @@ static void eevee_render_to_image(void *vedata, } } +static void eevee_store_metadata(void *vedata, struct RenderResult *render_result) +{ + EEVEE_Data *ved = (EEVEE_Data *)vedata; + EEVEE_PrivateData *g_data = ved->stl->g_data; + if (g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) { + EEVEE_cryptomatte_store_metadata(ved, render_result); + EEVEE_cryptomatte_free(ved); + } +} + static void eevee_engine_free(void) { EEVEE_shaders_free(); @@ -611,6 +619,7 @@ DrawEngineType draw_engine_eevee_type = { &eevee_view_update, &eevee_id_update, &eevee_render_to_image, + &eevee_store_metadata, }; RenderEngineType DRW_engine_viewport_eevee_type = { diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 5bf8cab1b22..9702db5fecc 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -977,6 +977,7 @@ typedef struct EEVEE_PrivateData { eViewLayerEEVEEPassType render_passes; int aov_hash; int num_aovs_used; + struct CryptomatteSession *cryptomatte_session; bool cryptomatte_accurate_mode; EEVEE_CryptomatteSample *cryptomatte_accum_buffer; float *cryptomatte_download_buffer; @@ -1246,6 +1247,7 @@ void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *sldata, int tot_samples); void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob); +void EEVEE_cryptomatte_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob); @@ -1261,6 +1263,7 @@ void EEVEE_cryptomatte_render_result(struct RenderLayer *rl, const rcti *rect, EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata); +void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, struct RenderResult *render_result); void EEVEE_cryptomatte_free(EEVEE_Data *vedata); /* eevee_occlusion.c */ diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index 2a01aeefce8..bb974688404 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -510,7 +510,6 @@ static void eevee_render_result_cryptomatte(RenderLayer *rl, if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) { EEVEE_cryptomatte_render_result(rl, viewname, rect, vedata, sldata); } - EEVEE_cryptomatte_free(vedata); } static void eevee_render_draw_background(EEVEE_Data *vedata) diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index dff3b437953..ce5292fbbb0 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -255,6 +255,10 @@ void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ve else { psl->renderpass_pass = NULL; } + + if ((g_data->render_passes & (EEVEE_RENDER_PASS_CRYPTOMATTE)) != 0) { + EEVEE_cryptomatte_cache_finish(sldata, vedata); + } } /* Post-process data to construct a specific render-pass diff --git a/source/blender/draw/engines/external/external_engine.c b/source/blender/draw/engines/external/external_engine.c index bd97fd8a413..1b331052a06 100644 --- a/source/blender/draw/engines/external/external_engine.c +++ b/source/blender/draw/engines/external/external_engine.c @@ -311,6 +311,7 @@ static DrawEngineType draw_engine_external_type = { NULL, NULL, NULL, + NULL, }; /* Note: currently unused, diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 519b015a6ad..20044fbe3ce 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -977,4 +977,5 @@ DrawEngineType draw_engine_gpencil_type = { NULL, NULL, &GPENCIL_render_to_image, + NULL, }; diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c index 0c602ba77c8..5445685b5ba 100644 --- a/source/blender/draw/engines/image/image_engine.c +++ b/source/blender/draw/engines/image/image_engine.c @@ -413,4 +413,5 @@ DrawEngineType draw_engine_image_type = { NULL, /* view_update */ NULL, /* id_update */ NULL, /* render_to_image */ + NULL, /* store_metadata */ }; diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 0fa3b17e7a5..f87f781b6ce 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -695,6 +695,7 @@ DrawEngineType draw_engine_overlay_type = { NULL, NULL, NULL, + NULL, }; /** \} */ diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index ec6e4c73043..c9c4a2076ef 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -370,6 +370,7 @@ DrawEngineType draw_engine_select_type = { NULL, NULL, NULL, + NULL, }; /* Note: currently unused, we may want to register so we can see this when debugging the view. */ diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 92ce5dddbc6..37dbfe4d2a6 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -641,6 +641,7 @@ DrawEngineType draw_engine_workbench = { &workbench_view_update, &workbench_id_update, &workbench_render, + NULL, }; RenderEngineType DRW_engine_viewport_workbench_type = { diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index f133f3bc6d7..4a43107c612 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -131,6 +131,7 @@ typedef struct DrawEngineType { struct RenderEngine *engine, struct RenderLayer *layer, const struct rcti *rect); + void (*store_metadata)(void *vedata, struct RenderResult *render_result); } DrawEngineType; /* Textures */ diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index db701c384bd..1115d819659 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1911,6 +1911,11 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) RE_engine_end_result(engine, render_result, false, false, false); + if (engine_type->draw_engine->store_metadata) { + RenderResult *final_render_result = RE_engine_get_result(engine); + engine_type->draw_engine->store_metadata(data, final_render_result); + } + /* Force cache to reset. */ drw_viewport_cache_resize();