diff options
Diffstat (limited to 'source/blender/compositor')
13 files changed, 283 insertions, 8 deletions
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 19eeb90c822..a226b009ec9 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -71,6 +71,8 @@ set(SRC intern/COM_MemoryBuffer.h intern/COM_MemoryProxy.cpp intern/COM_MemoryProxy.h + intern/COM_MetaData.cpp + intern/COM_MetaData.h intern/COM_Node.cpp intern/COM_Node.h intern/COM_NodeConverter.cpp diff --git a/source/blender/compositor/intern/COM_MetaData.cpp b/source/blender/compositor/intern/COM_MetaData.cpp new file mode 100644 index 00000000000..2b75947ff89 --- /dev/null +++ b/source/blender/compositor/intern/COM_MetaData.cpp @@ -0,0 +1,71 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_MetaData.h" + +#include "BKE_cryptomatte.hh" +#include "BKE_image.h" + +#include "RE_pipeline.h" + +#include <string_view> + +void MetaData::add(const blender::StringRef key, const blender::StringRef value) +{ + entries_.add(key, value); +} + +void MetaData::addCryptomatteEntry(const blender::StringRef layer_name, + const blender::StringRefNull key, + const blender::StringRef value) +{ + add(blender::BKE_cryptomatte_meta_data_key(layer_name, key), value); +} + +/* Replace the hash neutral cryptomatte keys with hashed versions. + * + * When a conversion happens it will also add the cryptomatte name key with the given + * `layer_name`.*/ +void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name) +{ + std::string cryptomatte_hash = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_HASH, ""); + std::string cryptomatte_conversion = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_CONVERSION, + ""); + std::string cryptomatte_manifest = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_MANIFEST, ""); + + if (cryptomatte_hash.length() || cryptomatte_conversion.length() || + cryptomatte_manifest.length()) { + addCryptomatteEntry(layer_name, "name", layer_name); + } + if (cryptomatte_hash.length()) { + addCryptomatteEntry(layer_name, "hash", cryptomatte_hash); + } + if (cryptomatte_conversion.length()) { + addCryptomatteEntry(layer_name, "conversion", cryptomatte_conversion); + } + if (cryptomatte_manifest.length()) { + addCryptomatteEntry(layer_name, "manifest", cryptomatte_manifest); + } +} + +void MetaData::addToRenderResult(RenderResult *render_result) const +{ + for (blender::Map<std::string, std::string>::Item entry : entries_.items()) { + BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str()); + } +} diff --git a/source/blender/compositor/intern/COM_MetaData.h b/source/blender/compositor/intern/COM_MetaData.h new file mode 100644 index 00000000000..22988b0b7ee --- /dev/null +++ b/source/blender/compositor/intern/COM_MetaData.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include <string> + +#include "BLI_map.hh" + +#include "MEM_guardedalloc.h" + +/* Forward declarations. */ +struct StampData; +struct RenderResult; + +/* Cryptomatte includes hash in its meta data keys. The hash is generated from the render + * layer/pass name. Compositing happens without the knowledge of the original layer and pass. The + * next keys are used to transfer the cryptomatte meta data in a neutral way. The file output node + * will generate a hash based on the layer name configured by the user. + * + * The `{hash}` has no special meaning except to make sure that the meta data stays unique. */ +constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_HASH("cryptomatte/{hash}/hash"); +constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_CONVERSION("cryptomatte/{hash}/conversion"); +constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_MANIFEST("cryptomatte/{hash}/manifest"); +constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_NAME("cryptomatte/{hash}/name"); + +class MetaData { + private: + blender::Map<std::string, std::string> entries_; + void addCryptomatteEntry(const blender::StringRef layer_name, + const blender::StringRefNull key, + const blender::StringRef value); + + public: + void add(const blender::StringRef key, const blender::StringRef value); + void replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name); + void addToRenderResult(RenderResult *render_result) const; +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:MetaData") +#endif +}; diff --git a/source/blender/compositor/intern/COM_SocketReader.h b/source/blender/compositor/intern/COM_SocketReader.h index ee2a6e0e1bf..7c4132efe60 100644 --- a/source/blender/compositor/intern/COM_SocketReader.h +++ b/source/blender/compositor/intern/COM_SocketReader.h @@ -19,8 +19,12 @@ #pragma once #include "BLI_rect.h" +#include "COM_MetaData.h" #include "COM_defines.h" +#include <memory> +#include <optional> + #ifdef WITH_CXX_GUARDEDALLOC # include "MEM_guardedalloc.h" #endif @@ -32,6 +36,7 @@ typedef enum PixelSampler { } PixelSampler; class MemoryBuffer; + /** * \brief Helper class for reading socket data. * Only use this class for dispatching (un-ary and n-ary) executions. @@ -134,6 +139,14 @@ class SocketReader { return this->m_height; } + /* Return the meta data associated with this branch. + * + * The return parameter holds an instance or is an nullptr. */ + virtual std::unique_ptr<MetaData> getMetaData() const + { + return std::unique_ptr<MetaData>(); + } + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:SocketReader") #endif diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cpp b/source/blender/compositor/nodes/COM_OutputFileNode.cpp index 09528e09f1f..e0cff1de276 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cpp +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cpp @@ -50,7 +50,8 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, OutputOpenExrMultiLayerOperation *outputOperation; if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) { - outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(context.getRenderData(), + outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(context.getScene(), + context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, @@ -58,7 +59,8 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, context.getViewName()); } else { - outputOperation = new OutputOpenExrMultiLayerOperation(context.getRenderData(), + outputOperation = new OutputOpenExrMultiLayerOperation(context.getScene(), + context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp index 21d9177ddd5..0b732357c92 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp @@ -143,13 +143,14 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() /************************************ OpenEXR Multilayer Multiview *******************************/ OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOperation( + const Scene *scene, const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, bool exr_half_float, const char *viewName) - : OutputOpenExrMultiLayerOperation(rd, tree, path, exr_codec, exr_half_float, viewName) + : OutputOpenExrMultiLayerOperation(scene, rd, tree, path, exr_codec, exr_half_float, viewName) { } @@ -195,12 +196,16 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename BLI_make_existing_file(filename); /* prepare the file with all the channels for the header */ - if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, nullptr) == 0) { + StampData *stamp_data = createStampData(); + if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data) == + 0) { printf("Error Writing Multilayer Multiview Openexr\n"); IMB_exr_close(exrhandle); + BKE_stamp_data_free(stamp_data); } else { IMB_exr_clear_channels(exrhandle); + BKE_stamp_data_free(stamp_data); return exrhandle; } } diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index 1deaf121173..bc057355cef 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -48,7 +48,8 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayerOperation { private: public: - OutputOpenExrMultiLayerMultiViewOperation(const RenderData *rd, + OutputOpenExrMultiLayerMultiViewOperation(const Scene *scene, + const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cpp b/source/blender/compositor/operations/COM_OutputFileOperation.cpp index 2676ab1b9ca..216b754f676 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cpp @@ -18,12 +18,15 @@ #include "COM_OutputFileOperation.h" +#include "COM_MetaData.h" + #include <cstring> #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BKE_cryptomatte.hh" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" @@ -36,6 +39,8 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "RE_pipeline.h" + void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, @@ -299,13 +304,15 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo this->imageInput = nullptr; } -OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const RenderData *rd, +OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *scene, + const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, bool exr_half_float, const char *viewName) { + this->m_scene = scene; this->m_rd = rd; this->m_tree = tree; @@ -323,6 +330,26 @@ void OutputOpenExrMultiLayerOperation::add_layer(const char *name, this->m_layers.push_back(OutputOpenExrLayer(name, datatype, use_layer)); } +StampData *OutputOpenExrMultiLayerOperation::createStampData() const +{ + /* StampData API doesn't provide functions to modify an instance without having a RenderResult. + */ + RenderResult render_result; + StampData *stamp_data = BKE_stamp_info_from_scene_static(m_scene); + render_result.stamp_data = stamp_data; + for (int i = 0; i < this->m_layers.size(); i++) { + const OutputOpenExrLayer *layer = &this->m_layers[i]; + std::unique_ptr<MetaData> meta_data = layer->imageInput->getMetaData(); + if (meta_data) { + blender::StringRef layer_name = blender::BKE_cryptomatte_extract_layer_name( + blender::StringRef(layer->name, BLI_strnlen(layer->name, sizeof(layer->name)))); + meta_data->replaceHashNeutralCryptomatteKeys(layer_name); + meta_data->addToRenderResult(&render_result); + } + } + return stamp_data; +} + void OutputOpenExrMultiLayerOperation::initExecution() { for (unsigned int i = 0; i < this->m_layers.size(); i++) { @@ -386,7 +413,8 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } /* when the filename has no permissions, this can fail */ - if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, nullptr)) { + StampData *stamp_data = createStampData(); + if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data)) { IMB_exr_write_channels(exrhandle); } else { @@ -404,5 +432,6 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() this->m_layers[i].imageInput = nullptr; } + BKE_stamp_data_free(stamp_data); } } diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index b2454e17e3f..915d59599e2 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -91,6 +91,7 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation { protected: typedef std::vector<OutputOpenExrLayer> LayerList; + const Scene *m_scene; const RenderData *m_rd; const bNodeTree *m_tree; @@ -100,8 +101,11 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation { LayerList m_layers; const char *m_viewName; + StampData *createStampData() const; + public: - OutputOpenExrMultiLayerOperation(const RenderData *rd, + OutputOpenExrMultiLayerOperation(const Scene *scene, + const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cpp b/source/blender/compositor/operations/COM_RenderLayersProg.cpp index 11f64aa4d6a..2a0a6e33b6a 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cpp +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cpp @@ -18,8 +18,16 @@ #include "COM_RenderLayersProg.h" +#include "COM_MetaData.h" + +#include "BKE_cryptomatte.hh" +#include "BKE_image.h" #include "BKE_scene.h" + #include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_ref.hh" + #include "DNA_scene_types.h" #include "RE_pipeline.h" @@ -209,6 +217,82 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2], } } +struct CallbackData { + std::unique_ptr<MetaData> meta_data; + std::string hash_key; + std::string conversion_key; + std::string manifest_key; + + void addMetaData(blender::StringRef key, blender::StringRefNull value) + { + if (!meta_data) { + meta_data = std::make_unique<MetaData>(); + } + meta_data->add(key, value); + } + + void setCryptomatteKeys(blender::StringRef cryptomatte_layer_name) + { + manifest_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "manifest"); + hash_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "hash"); + conversion_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "conversion"); + } +}; + +/* C type callback function (StampCallback). */ +static void extract_cryptomatte_meta_data(void *_data, + const char *propname, + char *propvalue, + int UNUSED(len)) +{ + CallbackData *data = static_cast<CallbackData *>(_data); + blender::StringRefNull key(propname); + if (key == data->hash_key) { + data->addMetaData(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue); + } + else if (key == data->conversion_key) { + data->addMetaData(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue); + } + else if (key == data->manifest_key) { + data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue); + } +} + +std::unique_ptr<MetaData> RenderLayersProg::getMetaData() const +{ + Scene *scene = this->getScene(); + Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr; + RenderResult *rr = nullptr; + CallbackData callback_data = {nullptr}; + + if (re) { + rr = RE_AcquireResultRead(re); + } + + if (rr && rr->stamp_data) { + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, getLayerId()); + if (view_layer) { + std::string full_layer_name = std::string( + view_layer->name, + BLI_strnlen(view_layer->name, sizeof(view_layer->name))) + + "." + m_passName; + blender::StringRef cryptomatte_layer_name = blender::BKE_cryptomatte_extract_layer_name( + full_layer_name); + callback_data.setCryptomatteKeys(cryptomatte_layer_name); + + BKE_stamp_info_callback( + &callback_data, rr->stamp_data, extract_cryptomatte_meta_data, false); + } + } + + if (re) { + RE_ReleaseResult(re); + re = nullptr; + } + + return std::move(callback_data.meta_data); +} + /* ******** Render Layers AO Operation ******** */ void RenderLayersAOOperation::executePixelSampled(float output[4], float x, diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index abfcac1f80d..ec98969b223 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -121,6 +121,8 @@ class RenderLayersProg : public NodeOperation { void initExecution(); void deinitExecution(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); + + std::unique_ptr<MetaData> getMetaData() const override; }; class RenderLayersAOOperation : public RenderLayersProg { diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.cpp b/source/blender/compositor/operations/COM_SocketProxyOperation.cpp index baeb2f44303..53f5fea8795 100644 --- a/source/blender/compositor/operations/COM_SocketProxyOperation.cpp +++ b/source/blender/compositor/operations/COM_SocketProxyOperation.cpp @@ -24,3 +24,8 @@ SocketProxyOperation::SocketProxyOperation(DataType type, bool use_conversion) this->addInputSocket(type); this->addOutputSocket(type); } + +std::unique_ptr<MetaData> SocketProxyOperation::getMetaData() const +{ + return this->getInputSocket(0)->getReader()->getMetaData(); +} diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.h b/source/blender/compositor/operations/COM_SocketProxyOperation.h index 22c144598f6..435083f5008 100644 --- a/source/blender/compositor/operations/COM_SocketProxyOperation.h +++ b/source/blender/compositor/operations/COM_SocketProxyOperation.h @@ -41,6 +41,7 @@ class SocketProxyOperation : public NodeOperation { { m_use_conversion = use_conversion; } + std::unique_ptr<MetaData> getMetaData() const override; private: bool m_use_conversion; |