Blender freezes when adding a Cryptomatte node and using the GPU compositor, even if the node is later removed. This was because the CPU compositor acquired a render result mutex and never released it due to an early exit that missed that release. This patch fixes that by adding an appropriate release in that early exist.
273 lines
9.6 KiB
C++
273 lines
9.6 KiB
C++
/* SPDX-FileCopyrightText: 2018 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "BLI_string.h"
|
|
|
|
#include "BKE_node.hh"
|
|
|
|
#include "NOD_composite.hh"
|
|
|
|
#include "COM_ConvertOperation.h"
|
|
#include "COM_CryptomatteNode.h"
|
|
#include "COM_MultilayerImageOperation.h"
|
|
#include "COM_RenderLayersProg.h"
|
|
#include "COM_SetAlphaMultiplyOperation.h"
|
|
#include "COM_SetAlphaReplaceOperation.h"
|
|
#include "COM_SetColorOperation.h"
|
|
|
|
namespace blender::compositor {
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Cryptomatte Base
|
|
* \{ */
|
|
|
|
void CryptomatteBaseNode::convert_to_operations(NodeConverter &converter,
|
|
const CompositorContext &context) const
|
|
{
|
|
NodeOutput *output_image_socket = this->get_output_socket(0);
|
|
|
|
const bNode *node = this->get_bnode();
|
|
const NodeCryptomatte *cryptomatte_settings = static_cast<const NodeCryptomatte *>(
|
|
node->storage);
|
|
|
|
CryptomatteOperation *cryptomatte_operation = create_cryptomatte_operation(
|
|
converter, context, *node, cryptomatte_settings);
|
|
converter.add_operation(cryptomatte_operation);
|
|
|
|
NodeOutput *output_matte_socket = this->get_output_socket(1);
|
|
SeparateChannelOperation *extract_mask_operation = new SeparateChannelOperation;
|
|
extract_mask_operation->set_channel(3);
|
|
converter.add_operation(extract_mask_operation);
|
|
converter.add_link(cryptomatte_operation->get_output_socket(0),
|
|
extract_mask_operation->get_input_socket(0));
|
|
converter.map_output_socket(output_matte_socket, extract_mask_operation->get_output_socket(0));
|
|
|
|
NodeInput *input_image_socket = this->get_input_socket(0);
|
|
SetAlphaMultiplyOperation *apply_mask_operation = new SetAlphaMultiplyOperation();
|
|
converter.map_input_socket(input_image_socket, apply_mask_operation->get_input_socket(0));
|
|
converter.add_operation(apply_mask_operation);
|
|
converter.add_link(extract_mask_operation->get_output_socket(0),
|
|
apply_mask_operation->get_input_socket(1));
|
|
converter.map_output_socket(output_image_socket, apply_mask_operation->get_output_socket(0));
|
|
|
|
NodeOutput *output_pick_socket = this->get_output_socket(2);
|
|
SetAlphaReplaceOperation *extract_pick_operation = new SetAlphaReplaceOperation();
|
|
converter.add_operation(extract_pick_operation);
|
|
converter.add_input_value(extract_pick_operation->get_input_socket(1), 1.0f);
|
|
converter.add_link(cryptomatte_operation->get_output_socket(0),
|
|
extract_pick_operation->get_input_socket(0));
|
|
converter.map_output_socket(output_pick_socket, extract_pick_operation->get_output_socket(0));
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Cryptomatte V2
|
|
* \{ */
|
|
|
|
static std::string prefix_from_node(const CompositorContext &context, const bNode &node)
|
|
{
|
|
char prefix[MAX_NAME];
|
|
ntreeCompositCryptomatteLayerPrefix(context.get_scene(), &node, prefix, sizeof(prefix));
|
|
return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix)));
|
|
}
|
|
|
|
static std::string combined_layer_pass_name(RenderLayer *render_layer, RenderPass *render_pass)
|
|
{
|
|
if (render_layer->name[0] == '\0') {
|
|
return std::string(render_pass->name,
|
|
BLI_strnlen(render_pass->name, sizeof(render_pass->name)));
|
|
}
|
|
|
|
std::string combined_name =
|
|
blender::StringRef(render_layer->name,
|
|
BLI_strnlen(render_layer->name, sizeof(render_layer->name))) +
|
|
"." +
|
|
blender::StringRef(render_pass->name,
|
|
BLI_strnlen(render_pass->name, sizeof(render_pass->name)));
|
|
return combined_name;
|
|
}
|
|
|
|
void CryptomatteNode::input_operations_from_render_source(
|
|
const CompositorContext &context,
|
|
const bNode &node,
|
|
Vector<NodeOperation *> &r_input_operations)
|
|
{
|
|
Scene *scene = (Scene *)node.id;
|
|
if (!scene) {
|
|
return;
|
|
}
|
|
|
|
BLI_assert(GS(scene->id.name) == ID_SCE);
|
|
Render *render = RE_GetSceneRender(scene);
|
|
RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
|
|
|
|
if (!render_result) {
|
|
if (render) {
|
|
RE_ReleaseResult(render);
|
|
}
|
|
return;
|
|
}
|
|
|
|
short view_layer_id = 0;
|
|
const std::string prefix = prefix_from_node(context, node);
|
|
LISTBASE_FOREACH_INDEX (ViewLayer *, view_layer, &scene->view_layers, view_layer_id) {
|
|
RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name);
|
|
if (render_layer) {
|
|
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
|
|
if (context.has_explicit_view() && !STREQ(render_pass->view, context.get_view_name())) {
|
|
continue;
|
|
}
|
|
|
|
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
|
|
if (blender::StringRef(combined_name).startswith(prefix)) {
|
|
RenderLayersProg *op = new RenderLayersProg(
|
|
render_pass->name, DataType::Color, render_pass->channels);
|
|
op->set_scene(scene);
|
|
op->set_layer_id(view_layer_id);
|
|
op->set_render_data(context.get_render_data());
|
|
op->set_view_name(context.get_view_name());
|
|
r_input_operations.append(op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RE_ReleaseResult(render);
|
|
}
|
|
|
|
void CryptomatteNode::input_operations_from_image_source(
|
|
const CompositorContext &context,
|
|
const bNode &node,
|
|
Vector<NodeOperation *> &r_input_operations)
|
|
{
|
|
NodeCryptomatte *cryptomatte_settings = (NodeCryptomatte *)node.storage;
|
|
Image *image = (Image *)node.id;
|
|
if (!image) {
|
|
return;
|
|
}
|
|
|
|
BLI_assert(GS(image->id.name) == ID_IM);
|
|
if (image->type != IMA_TYPE_MULTILAYER) {
|
|
return;
|
|
}
|
|
|
|
ImageUser *iuser = &cryptomatte_settings->iuser;
|
|
BKE_image_user_frame_calc(image, iuser, context.get_framenumber());
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr);
|
|
|
|
if (image->rr) {
|
|
int view = 0;
|
|
if (BLI_listbase_count_at_most(&image->rr->views, 2) > 1) {
|
|
if (iuser->view == 0) {
|
|
/* Heuristic to match image name with scene names, check if the view name exists in the
|
|
* image. */
|
|
view = BLI_findstringindex(
|
|
&image->rr->views, context.get_view_name(), offsetof(RenderView, name));
|
|
if (view == -1) {
|
|
view = 0;
|
|
}
|
|
}
|
|
else {
|
|
view = iuser->view - 1;
|
|
}
|
|
}
|
|
|
|
const std::string prefix = prefix_from_node(context, node);
|
|
int layer_index;
|
|
LISTBASE_FOREACH_INDEX (RenderLayer *, render_layer, &image->rr->layers, layer_index) {
|
|
if (!blender::StringRef(prefix).startswith(blender::StringRef(
|
|
render_layer->name, BLI_strnlen(render_layer->name, sizeof(render_layer->name)))))
|
|
{
|
|
continue;
|
|
}
|
|
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
|
|
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
|
|
if (blender::StringRef(combined_name).startswith(prefix)) {
|
|
MultilayerColorOperation *op = new MultilayerColorOperation(
|
|
render_layer, render_pass, view);
|
|
op->set_image(image);
|
|
op->set_image_user(iuser);
|
|
iuser->layer = layer_index;
|
|
op->set_framenumber(context.get_framenumber());
|
|
r_input_operations.append(op);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
BKE_image_release_ibuf(image, ibuf, nullptr);
|
|
}
|
|
|
|
Vector<NodeOperation *> CryptomatteNode::create_input_operations(const CompositorContext &context,
|
|
const bNode &node)
|
|
{
|
|
Vector<NodeOperation *> input_operations;
|
|
switch (node.custom1) {
|
|
case CMP_CRYPTOMATTE_SRC_RENDER:
|
|
input_operations_from_render_source(context, node, input_operations);
|
|
break;
|
|
case CMP_CRYPTOMATTE_SRC_IMAGE:
|
|
input_operations_from_image_source(context, node, input_operations);
|
|
break;
|
|
}
|
|
|
|
if (input_operations.is_empty()) {
|
|
SetColorOperation *op = new SetColorOperation();
|
|
op->set_channel1(0.0f);
|
|
op->set_channel2(1.0f);
|
|
op->set_channel3(0.0f);
|
|
op->set_channel4(0.0f);
|
|
input_operations.append(op);
|
|
}
|
|
return input_operations;
|
|
}
|
|
CryptomatteOperation *CryptomatteNode::create_cryptomatte_operation(
|
|
NodeConverter &converter,
|
|
const CompositorContext &context,
|
|
const bNode &node,
|
|
const NodeCryptomatte *cryptomatte_settings) const
|
|
{
|
|
Vector<NodeOperation *> input_operations = create_input_operations(context, node);
|
|
CryptomatteOperation *operation = new CryptomatteOperation(input_operations.size());
|
|
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) {
|
|
operation->add_object_index(cryptomatte_entry->encoded_hash);
|
|
}
|
|
for (int i = 0; i < input_operations.size(); ++i) {
|
|
converter.add_operation(input_operations[i]);
|
|
converter.add_link(input_operations[i]->get_output_socket(), operation->get_input_socket(i));
|
|
}
|
|
return operation;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Cryptomatte Legacy
|
|
* \{ */
|
|
|
|
CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation(
|
|
NodeConverter &converter,
|
|
const CompositorContext & /*context*/,
|
|
const bNode & /*node*/,
|
|
const NodeCryptomatte *cryptomatte_settings) const
|
|
{
|
|
const int num_inputs = inputs_.size() - 1;
|
|
CryptomatteOperation *operation = new CryptomatteOperation(num_inputs);
|
|
if (cryptomatte_settings) {
|
|
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) {
|
|
operation->add_object_index(cryptomatte_entry->encoded_hash);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < num_inputs; i++) {
|
|
converter.map_input_socket(this->get_input_socket(i + 1), operation->get_input_socket(i));
|
|
}
|
|
|
|
return operation;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
} // namespace blender::compositor
|