Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2021-06-14 13:44:13 +0300
committerJacques Lucke <jacques@blender.org>2021-06-14 13:44:13 +0300
commitdddcf1e9bbf4a6d1f4ff03eaf0cb7e9228b18ec5 (patch)
treec20defa7efd54c933d20a296abefe567909bb6c0 /source/blender/compositor/intern
parent3b162b7c185d089e93d892169a458d552196b7b6 (diff)
parentc9dc55301cd7903b7ef7c045d337ada29aa809a1 (diff)
Merge branch 'master' into temp-compact-node-prototypetemp-compact-node-prototype
Diffstat (limited to 'source/blender/compositor/intern')
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.cc65
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.h (renamed from source/blender/compositor/intern/COM_SocketReader.cc)22
-rw-r--r--source/blender/compositor/intern/COM_CPUDevice.cc33
-rw-r--r--source/blender/compositor/intern/COM_CPUDevice.h4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrder.cc4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrder.h4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrderHotspot.cc4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrderHotspot.h4
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.cc31
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h24
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc36
-rw-r--r--source/blender/compositor/intern/COM_Converter.h7
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc185
-rw-r--r--source/blender/compositor/intern/COM_Debug.h105
-rw-r--r--source/blender/compositor/intern/COM_Device.h12
-rw-r--r--source/blender/compositor/intern/COM_Enums.cc61
-rw-r--r--source/blender/compositor/intern/COM_Enums.h91
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.cc217
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.h159
-rw-r--r--source/blender/compositor/intern/COM_ExecutionModel.cc48
-rw-r--r--source/blender/compositor/intern/COM_ExecutionModel.h84
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.cc155
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.h49
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc362
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.h88
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc104
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h159
-rw-r--r--source/blender/compositor/intern/COM_MemoryProxy.cc8
-rw-r--r--source/blender/compositor/intern/COM_MemoryProxy.h19
-rw-r--r--source/blender/compositor/intern/COM_MetaData.cc6
-rw-r--r--source/blender/compositor/intern/COM_MetaData.h6
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.cc26
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.h73
-rw-r--r--source/blender/compositor/intern/COM_Node.cc24
-rw-r--r--source/blender/compositor/intern/COM_Node.h73
-rw-r--r--source/blender/compositor/intern/COM_NodeConverter.cc4
-rw-r--r--source/blender/compositor/intern/COM_NodeConverter.h4
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.cc46
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.h21
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.cc343
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h622
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc119
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.h30
-rw-r--r--source/blender/compositor/intern/COM_OpenCLDevice.cc26
-rw-r--r--source/blender/compositor/intern/COM_OpenCLDevice.h7
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.cc129
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.h71
-rw-r--r--source/blender/compositor/intern/COM_SingleThreadedOperation.cc7
-rw-r--r--source/blender/compositor/intern/COM_SingleThreadedOperation.h9
-rw-r--r--source/blender/compositor/intern/COM_SocketReader.h153
-rw-r--r--source/blender/compositor/intern/COM_TiledExecutionModel.cc158
-rw-r--r--source/blender/compositor/intern/COM_TiledExecutionModel.h54
-rw-r--r--source/blender/compositor/intern/COM_WorkPackage.cc18
-rw-r--r--source/blender/compositor/intern/COM_WorkPackage.h38
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.cc42
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.h12
-rw-r--r--source/blender/compositor/intern/COM_compositor.cc16
57 files changed, 3057 insertions, 1224 deletions
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc
new file mode 100644
index 00000000000..8464d01801f
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferOperation.cc
@@ -0,0 +1,65 @@
+/*
+ * 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_BufferOperation.h"
+
+namespace blender::compositor {
+
+BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
+{
+ buffer_ = buffer;
+ /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following
+ * code to: set_resolution(buffer.get_size()) */
+ unsigned int resolution[2];
+ resolution[0] = buffer->getWidth();
+ resolution[1] = buffer->getHeight();
+ setResolution(resolution);
+ addOutputSocket(data_type);
+}
+
+void *BufferOperation::initializeTileData(rcti * /*rect*/)
+{
+ return buffer_;
+}
+
+void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
+{
+ switch (sampler) {
+ case PixelSampler::Nearest:
+ buffer_->read(output, x, y);
+ break;
+ case PixelSampler::Bilinear:
+ default:
+ buffer_->readBilinear(output, x, y);
+ break;
+ case PixelSampler::Bicubic:
+ /* No bicubic. Same implementation as ReadBufferOperation. */
+ buffer_->readBilinear(output, x, y);
+ break;
+ }
+}
+
+void BufferOperation::executePixelFiltered(
+ float output[4], float x, float y, float dx[2], float dy[2])
+{
+ const float uv[2] = {x, y};
+ const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
+ buffer_->readEWA(output, uv, deriv);
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SocketReader.cc b/source/blender/compositor/intern/COM_BufferOperation.h
index 93c8a143b86..f87cd4db94e 100644
--- a/source/blender/compositor/intern/COM_SocketReader.cc
+++ b/source/blender/compositor/intern/COM_BufferOperation.h
@@ -13,7 +13,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Copyright 2011, Blender Foundation.
+ * Copyright 2021, Blender Foundation.
*/
-#include "COM_SocketReader.h"
+#pragma once
+
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+class BufferOperation : public NodeOperation {
+ private:
+ MemoryBuffer *buffer_;
+
+ public:
+ BufferOperation(MemoryBuffer *buffer, DataType data_type);
+
+ void *initializeTileData(rcti *rect) override;
+ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc
index b520a437008..2ca5557e278 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.cc
+++ b/source/blender/compositor/intern/COM_CPUDevice.cc
@@ -18,19 +18,36 @@
#include "COM_CPUDevice.h"
+#include "COM_ExecutionGroup.h"
+
+#include "BLI_rect.h"
+
+namespace blender::compositor {
+
CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id)
{
}
-void CPUDevice::execute(WorkPackage *work)
+void CPUDevice::execute(WorkPackage *work_package)
{
- const unsigned int chunkNumber = work->chunk_number;
- ExecutionGroup *executionGroup = work->execution_group;
- rcti rect;
+ switch (work_package->type) {
+ case eWorkPackageType::Tile: {
+ const unsigned int chunkNumber = work_package->chunk_number;
+ ExecutionGroup *executionGroup = work_package->execution_group;
- executionGroup->determineChunkRect(&rect, chunkNumber);
+ executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber);
+ executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+ break;
+ }
+ case eWorkPackageType::CustomFunction: {
+ work_package->execute_fn();
+ break;
+ }
+ }
- executionGroup->getOutputOperation()->executeRegion(&rect, chunkNumber);
-
- executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+ if (work_package->executed_fn) {
+ work_package->executed_fn();
+ }
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CPUDevice.h b/source/blender/compositor/intern/COM_CPUDevice.h
index 6df1f41419d..99629890b30 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.h
+++ b/source/blender/compositor/intern/COM_CPUDevice.h
@@ -20,6 +20,8 @@
#include "COM_Device.h"
+namespace blender::compositor {
+
/**
* \brief class representing a CPU device.
* \note for every hardware thread in the system a CPUDevice instance
@@ -43,3 +45,5 @@ class CPUDevice : public Device {
protected:
int m_thread_id;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrder.cc b/source/blender/compositor/intern/COM_ChunkOrder.cc
index 9687154120d..a03718d720d 100644
--- a/source/blender/compositor/intern/COM_ChunkOrder.cc
+++ b/source/blender/compositor/intern/COM_ChunkOrder.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
void ChunkOrder::update_distance(ChunkOrderHotspot *hotspots, unsigned int len_hotspots)
{
double new_distance = DBL_MAX;
@@ -36,3 +38,5 @@ bool operator<(const ChunkOrder &a, const ChunkOrder &b)
{
return a.distance < b.distance;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrder.h b/source/blender/compositor/intern/COM_ChunkOrder.h
index a634309f345..a697f9231d9 100644
--- a/source/blender/compositor/intern/COM_ChunkOrder.h
+++ b/source/blender/compositor/intern/COM_ChunkOrder.h
@@ -24,6 +24,8 @@
#include "COM_ChunkOrderHotspot.h"
+namespace blender::compositor {
+
/** Helper to determine the order how chunks are prioritized during execution. */
struct ChunkOrder {
unsigned int index = 0;
@@ -39,3 +41,5 @@ struct ChunkOrder {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ChunkOrderHotspot")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
index d31ff518ecd..b8e19fc2c34 100644
--- a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
+++ b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
@@ -19,6 +19,8 @@
#include "COM_ChunkOrderHotspot.h"
#include <cmath>
+namespace blender::compositor {
+
double ChunkOrderHotspot::calc_distance(int x, int y)
{
int dx = this->x - x;
@@ -27,3 +29,5 @@ double ChunkOrderHotspot::calc_distance(int x, int y)
result += (double)this->addition;
return result;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrderHotspot.h b/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
index d7f40921836..249b328f5c2 100644
--- a/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
+++ b/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
@@ -22,6 +22,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
struct ChunkOrderHotspot {
int x;
int y;
@@ -37,3 +39,5 @@ struct ChunkOrderHotspot {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ChunkOrderHotspot")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc
index e2447fb5c13..61e299c045e 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.cc
+++ b/source/blender/compositor/intern/COM_CompositorContext.cc
@@ -20,22 +20,43 @@
#include "COM_defines.h"
#include <cstdio>
+#include "BLI_assert.h"
+#include "DNA_userdef_types.h"
+
+namespace blender::compositor {
+
CompositorContext::CompositorContext()
{
this->m_scene = nullptr;
this->m_rd = nullptr;
- this->m_quality = CompositorQuality::High;
+ this->m_quality = eCompositorQuality::High;
this->m_hasActiveOpenCLDevices = false;
this->m_fastCalculation = false;
this->m_viewSettings = nullptr;
this->m_displaySettings = nullptr;
+ this->m_bnodetree = nullptr;
}
int CompositorContext::getFramenumber() const
{
- if (this->m_rd) {
- return this->m_rd->cfra;
- }
+ BLI_assert(m_rd);
+ return m_rd->cfra;
+}
- return -1; /* this should never happen */
+eExecutionModel CompositorContext::get_execution_model() const
+{
+ if (U.experimental.use_full_frame_compositor) {
+ BLI_assert(m_bnodetree != nullptr);
+ switch (m_bnodetree->execution_mode) {
+ case 1:
+ return eExecutionModel::FullFrame;
+ case 0:
+ return eExecutionModel::Tiled;
+ default:
+ BLI_assert(!"Invalid execution mode");
+ }
+ }
+ return eExecutionModel::Tiled;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index 46cf65bbb79..56251511576 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -19,13 +19,18 @@
#pragma once
#include "BLI_rect.h"
-#include "COM_defines.h"
+
+#include "COM_Enums.h"
+
#include "DNA_color_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
+
#include <string>
#include <vector>
+namespace blender::compositor {
+
/**
* \brief Overall context of the compositor
*/
@@ -33,8 +38,8 @@ class CompositorContext {
private:
/**
* \brief The rendering field describes if we are rendering (F12) or if we are editing (Node
- * editor) This field is initialized in ExecutionSystem and must only be read from that point on.
- * \see ExecutionSystem
+ * editor) This field is initialized in ExecutionSystem and must only be read from that point
+ * on. \see ExecutionSystem
*/
bool m_rendering;
@@ -43,7 +48,7 @@ class CompositorContext {
* This field is initialized in ExecutionSystem and must only be read from that point on.
* \see ExecutionSystem
*/
- CompositorQuality m_quality;
+ eCompositorQuality m_quality;
Scene *m_scene;
@@ -200,7 +205,7 @@ class CompositorContext {
/**
* \brief set the quality
*/
- void setQuality(CompositorQuality quality)
+ void setQuality(eCompositorQuality quality)
{
this->m_quality = quality;
}
@@ -208,7 +213,7 @@ class CompositorContext {
/**
* \brief get the quality
*/
- CompositorQuality getQuality() const
+ eCompositorQuality getQuality() const
{
return this->m_quality;
}
@@ -276,4 +281,11 @@ class CompositorContext {
{
return m_rd->size * 0.01f;
}
+
+ /**
+ * Get active execution model.
+ */
+ eExecutionModel get_execution_model() const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index d5bce636b8c..af593b2e1b5 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -26,6 +26,7 @@
#include "COM_NodeOperationBuilder.h"
#include "COM_AlphaOverNode.h"
+#include "COM_AntiAliasingNode.h"
#include "COM_BilateralBlurNode.h"
#include "COM_BlurNode.h"
#include "COM_BokehBlurNode.h"
@@ -115,6 +116,8 @@
#include "COM_ViewerNode.h"
#include "COM_ZCombineNode.h"
+namespace blender::compositor {
+
bool COM_bnode_is_fast_node(const bNode &b_node)
{
return !ELEM(b_node.type,
@@ -418,6 +421,9 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_EXPOSURE:
node = new ExposureNode(b_node);
break;
+ case CMP_NODE_ANTIALIASING:
+ node = new AntiAliasingNode(b_node);
+ break;
}
return node;
}
@@ -454,7 +460,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket)
{
- InputResizeMode mode = toSocket->getResizeMode();
+ ResizeMode mode = toSocket->getResizeMode();
NodeOperation *toOperation = &toSocket->getOperation();
const float toWidth = toOperation->getWidth();
@@ -470,22 +476,22 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
float scaleY = 0;
switch (mode) {
- case COM_SC_NO_RESIZE:
+ case ResizeMode::None:
break;
- case COM_SC_CENTER:
+ case ResizeMode::Center:
doCenter = true;
break;
- case COM_SC_FIT_WIDTH:
+ case ResizeMode::FitWidth:
doCenter = true;
doScale = true;
scaleX = scaleY = toWidth / fromWidth;
break;
- case COM_SC_FIT_HEIGHT:
+ case ResizeMode::FitHeight:
doCenter = true;
doScale = true;
scaleX = scaleY = toHeight / fromHeight;
break;
- case COM_SC_FIT:
+ case ResizeMode::FitAny:
doCenter = true;
doScale = true;
scaleX = toWidth / fromWidth;
@@ -497,7 +503,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
scaleY = scaleX;
}
break;
- case COM_SC_STRETCH:
+ case ResizeMode::Stretch:
doCenter = true;
doScale = true;
scaleX = toWidth / fromWidth;
@@ -510,8 +516,8 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
ScaleOperation *scaleOperation = nullptr;
if (doScale) {
scaleOperation = new ScaleOperation();
- scaleOperation->getInputSocket(1)->setResizeMode(COM_SC_NO_RESIZE);
- scaleOperation->getInputSocket(2)->setResizeMode(COM_SC_NO_RESIZE);
+ scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
first = scaleOperation;
SetValueOperation *sxop = new SetValueOperation();
sxop->setValue(scaleX);
@@ -530,8 +536,8 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
}
TranslateOperation *translateOperation = new TranslateOperation();
- translateOperation->getInputSocket(1)->setResizeMode(COM_SC_NO_RESIZE);
- translateOperation->getInputSocket(2)->setResizeMode(COM_SC_NO_RESIZE);
+ translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
if (!first) {
first = translateOperation;
}
@@ -551,15 +557,17 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
builder.addOperation(translateOperation);
if (doScale) {
- translateOperation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
}
/* remove previous link and replace */
builder.removeInputLink(toSocket);
- first->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
- toSocket->setResizeMode(COM_SC_NO_RESIZE);
+ first->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ toSocket->setResizeMode(ResizeMode::None);
builder.addLink(fromSocket, first->getInputSocket(0));
builder.addLink(translateOperation->getOutputSocket(), toSocket);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h
index 59be34bf0e3..28136437103 100644
--- a/source/blender/compositor/intern/COM_Converter.h
+++ b/source/blender/compositor/intern/COM_Converter.h
@@ -18,14 +18,17 @@
#pragma once
+#include "COM_NodeOperation.h"
+
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
#endif
struct bNode;
+namespace blender::compositor {
+
class Node;
-class NodeOperation;
class NodeOperationInput;
class NodeOperationOutput;
class NodeOperationBuilder;
@@ -65,3 +68,5 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from,
void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index c299bd1c72d..4cf7e09a7d8 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -18,30 +18,29 @@
#include "COM_Debug.h"
-#ifdef COM_DEBUG
-
-# include <map>
-# include <typeinfo>
-# include <vector>
+#include <map>
+#include <typeinfo>
+#include <vector>
extern "C" {
-# include "BLI_fileops.h"
-# include "BLI_path_util.h"
-# include "BLI_string.h"
-# include "BLI_sys_types.h"
-
-# include "BKE_appdir.h"
-# include "BKE_node.h"
-# include "DNA_node_types.h"
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_sys_types.h"
+
+#include "BKE_appdir.h"
+#include "BKE_node.h"
+#include "DNA_node_types.h"
}
-# include "COM_ExecutionGroup.h"
-# include "COM_ExecutionSystem.h"
-# include "COM_Node.h"
+#include "COM_ExecutionSystem.h"
+#include "COM_Node.h"
+
+#include "COM_ReadBufferOperation.h"
+#include "COM_ViewerOperation.h"
+#include "COM_WriteBufferOperation.h"
-# include "COM_ReadBufferOperation.h"
-# include "COM_ViewerOperation.h"
-# include "COM_WriteBufferOperation.h"
+namespace blender::compositor {
int DebugInfo::m_file_index = 0;
DebugInfo::NodeNameMap DebugInfo::m_node_names;
@@ -68,52 +67,8 @@ std::string DebugInfo::operation_name(const NodeOperation *op)
return "";
}
-void DebugInfo::convert_started()
-{
- m_op_names.clear();
-}
-
-void DebugInfo::execute_started(const ExecutionSystem *system)
-{
- m_file_index = 1;
- m_group_states.clear();
- for (ExecutionGroup *execution_group : system->m_groups) {
- m_group_states[execution_group] = EG_WAIT;
- }
-}
-
-void DebugInfo::node_added(const Node *node)
-{
- m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
-}
-
-void DebugInfo::node_to_operations(const Node *node)
-{
- m_current_node_name = m_node_names[node];
-}
-
-void DebugInfo::operation_added(const NodeOperation *operation)
-{
- m_op_names[operation] = m_current_node_name;
-}
-
-void DebugInfo::operation_read_write_buffer(const NodeOperation *operation)
-{
- m_current_op_name = m_op_names[operation];
-}
-
-void DebugInfo::execution_group_started(const ExecutionGroup *group)
-{
- m_group_states[group] = EG_RUNNING;
-}
-
-void DebugInfo::execution_group_finished(const ExecutionGroup *group)
-{
- m_group_states[group] = EG_FINISHED;
-}
-
int DebugInfo::graphviz_operation(const ExecutionSystem *system,
- const NodeOperation *operation,
+ NodeOperation *operation,
const ExecutionGroup *group,
char *str,
int maxlen)
@@ -121,7 +76,7 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
int len = 0;
std::string fillcolor = "gainsboro";
- if (operation->isViewerOperation()) {
+ if (operation->get_flags().is_viewer_operation) {
const ViewerOperation *viewer = (const ViewerOperation *)operation;
if (viewer->isActiveViewerOutput()) {
fillcolor = "lightskyblue1";
@@ -133,13 +88,13 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
else if (operation->isOutputOperation(system->getContext().isRendering())) {
fillcolor = "dodgerblue1";
}
- else if (operation->isSetOperation()) {
+ else if (operation->get_flags().is_set_operation) {
fillcolor = "khaki1";
}
- else if (operation->isReadBufferOperation()) {
+ else if (operation->get_flags().is_read_buffer_operation) {
fillcolor = "darkolivegreen3";
}
- else if (operation->isWriteBufferOperation()) {
+ else if (operation->get_flags().is_write_buffer_operation) {
fillcolor = "darkorange";
}
@@ -256,12 +211,14 @@ int DebugInfo::graphviz_legend_group(
return len;
}
-int DebugInfo::graphviz_legend(char *str, int maxlen)
+int DebugInfo::graphviz_legend(char *str, int maxlen, const bool has_execution_groups)
{
int len = 0;
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n");
- len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n");
+ if (has_execution_groups) {
+ len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n");
+ }
len += snprintf(
str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n");
@@ -281,21 +238,24 @@ int DebugInfo::graphviz_legend(char *str, int maxlen)
"Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0);
len += graphviz_legend_color(
"Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_color(
- "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_color(
- "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0);
+ if (has_execution_groups) {
+ len += graphviz_legend_color(
+ "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_color(
+ "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0);
+ }
len += graphviz_legend_color(
"Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0);
- len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n");
-
- len += graphviz_legend_group(
- "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_group(
- "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_group(
- "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ if (has_execution_groups) {
+ len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n");
+ len += graphviz_legend_group(
+ "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_group(
+ "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_group(
+ "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ }
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "</TABLE>\r\n");
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n");
@@ -360,7 +320,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
for (NodeOperation *operation : system->m_operations) {
- if (operation->isReadBufferOperation()) {
+ if (operation->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read = (ReadBufferOperation *)operation;
WriteBufferOperation *write = read->getMemoryProxy()->getWriteBufferOperation();
std::vector<std::string> &read_groups = op_groups[read];
@@ -381,8 +341,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
for (NodeOperation *op : system->m_operations) {
- for (NodeOperationInput *to : op->m_inputs) {
- NodeOperationOutput *from = to->getLink();
+ for (NodeOperationInput &to : op->m_inputs) {
+ NodeOperationOutput *from = to.getLink();
if (!from) {
continue;
@@ -401,7 +361,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
break;
}
- NodeOperation *to_op = &to->getOperation();
+ NodeOperation *to_op = &to.getOperation();
NodeOperation *from_op = &from->getOperation();
std::vector<std::string> &from_groups = op_groups[from_op];
std::vector<std::string> &to_groups = op_groups[to_op];
@@ -412,7 +372,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
from_op,
from,
to_op,
- to);
+ &to);
for (int k = 0; k < from_groups.size(); k++) {
for (int l = 0; l < to_groups.size(); l++) {
len += snprintf(str + len,
@@ -423,7 +383,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
from,
to_op,
to_groups[l].c_str(),
- to);
+ &to);
len += snprintf(
str + len, maxlen > len ? maxlen - len : 0, " [color=%s]", color.c_str());
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
@@ -432,7 +392,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
}
- len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0);
+ const bool has_execution_groups = system->getContext().get_execution_model() ==
+ eExecutionModel::Tiled;
+ len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups);
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
@@ -441,6 +403,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
void DebugInfo::graphviz(const ExecutionSystem *system)
{
+ if (!COM_EXPORT_GRAPHVIZ) {
+ return;
+ }
char str[1000000];
if (graphviz_system(system, str, sizeof(str) - 1)) {
char basename[FILE_MAX];
@@ -450,48 +415,12 @@ void DebugInfo::graphviz(const ExecutionSystem *system)
BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename);
m_file_index++;
+ std::cout << "Writing compositor debug to: " << filename << "\n";
+
FILE *fp = BLI_fopen(filename, "wb");
fputs(str, fp);
fclose(fp);
}
}
-#else
-
-std::string DebugInfo::node_name(const Node * /*node*/)
-{
- return "";
-}
-std::string DebugInfo::operation_name(const NodeOperation * /*op*/)
-{
- return "";
-}
-void DebugInfo::convert_started()
-{
-}
-void DebugInfo::execute_started(const ExecutionSystem * /*system*/)
-{
-}
-void DebugInfo::node_added(const Node * /*node*/)
-{
-}
-void DebugInfo::node_to_operations(const Node * /*node*/)
-{
-}
-void DebugInfo::operation_added(const NodeOperation * /*operation*/)
-{
-}
-void DebugInfo::operation_read_write_buffer(const NodeOperation * /*operation*/)
-{
-}
-void DebugInfo::execution_group_started(const ExecutionGroup * /*group*/)
-{
-}
-void DebugInfo::execution_group_finished(const ExecutionGroup * /*group*/)
-{
-}
-void DebugInfo::graphviz(const ExecutionSystem * /*system*/)
-{
-}
-
-#endif
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index 0107d8b396d..0de3a5e39dc 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -21,10 +21,14 @@
#include <map>
#include <string>
+#include "COM_ExecutionSystem.h"
+#include "COM_NodeOperation.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
+static constexpr bool COM_EXPORT_GRAPHVIZ = false;
class Node;
-class NodeOperation;
class ExecutionSystem;
class ExecutionGroup;
@@ -39,23 +43,84 @@ class DebugInfo {
static std::string node_name(const Node *node);
static std::string operation_name(const NodeOperation *op);
- static void convert_started();
- static void execute_started(const ExecutionSystem *system);
+ private:
+ static int m_file_index;
+ /** Map nodes to usable names for debug output. */
+ static NodeNameMap m_node_names;
+ /** Map operations to usable names for debug output. */
+ static OpNameMap m_op_names;
+ /** Base name for all operations added by a node. */
+ static std::string m_current_node_name;
+ /** Base name for automatic sub-operations. */
+ static std::string m_current_op_name;
+ /** For visualizing group states. */
+ static GroupStateMap m_group_states;
+
+ public:
+ static void convert_started()
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_op_names.clear();
+ }
+ }
+
+ static void execute_started(const ExecutionSystem *system)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_file_index = 1;
+ m_group_states.clear();
+ for (ExecutionGroup *execution_group : system->m_groups) {
+ m_group_states[execution_group] = EG_WAIT;
+ }
+ }
+ };
+
+ static void node_added(const Node *node)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
+ }
+ }
- static void node_added(const Node *node);
- static void node_to_operations(const Node *node);
- static void operation_added(const NodeOperation *operation);
- static void operation_read_write_buffer(const NodeOperation *operation);
+ static void node_to_operations(const Node *node)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_current_node_name = m_node_names[node];
+ }
+ }
- static void execution_group_started(const ExecutionGroup *group);
- static void execution_group_finished(const ExecutionGroup *group);
+ static void operation_added(const NodeOperation *operation)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_op_names[operation] = m_current_node_name;
+ }
+ };
+
+ static void operation_read_write_buffer(const NodeOperation *operation)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_current_op_name = m_op_names[operation];
+ }
+ };
+
+ static void execution_group_started(const ExecutionGroup *group)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_group_states[group] = EG_RUNNING;
+ }
+ };
+ static void execution_group_finished(const ExecutionGroup *group)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_group_states[group] = EG_FINISHED;
+ }
+ };
static void graphviz(const ExecutionSystem *system);
-#ifdef COM_DEBUG
protected:
static int graphviz_operation(const ExecutionSystem *system,
- const NodeOperation *operation,
+ NodeOperation *operation,
const ExecutionGroup *group,
char *str,
int maxlen);
@@ -64,20 +129,8 @@ class DebugInfo {
const char *name, const char *color, const char *style, char *str, int maxlen);
static int graphviz_legend_group(
const char *name, const char *color, const char *style, char *str, int maxlen);
- static int graphviz_legend(char *str, int maxlen);
+ static int graphviz_legend(char *str, int maxlen, bool has_execution_groups);
static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen);
-
- private:
- static int m_file_index;
- /** Map nodes to usable names for debug output. */
- static NodeNameMap m_node_names;
- /** Map operations to usable names for debug output. */
- static OpNameMap m_op_names;
- /** Base name for all operations added by a node. */
- static std::string m_current_node_name;
- /** Base name for automatic sub-operations. */
- static std::string m_current_op_name;
- /** For visualizing group states. */
- static GroupStateMap m_group_states;
-#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Device.h b/source/blender/compositor/intern/COM_Device.h
index 0a456760045..c848672a405 100644
--- a/source/blender/compositor/intern/COM_Device.h
+++ b/source/blender/compositor/intern/COM_Device.h
@@ -20,6 +20,8 @@
#include "COM_WorkPackage.h"
+namespace blender::compositor {
+
/**
* \brief Abstract class for device implementations to be used by the Compositor.
* devices are queried, initialized and used by the WorkScheduler.
@@ -28,6 +30,14 @@
class Device {
public:
+ Device() = default;
+
+ Device(const Device &other) = delete;
+ Device(Device &&other) noexcept = default;
+
+ Device &operator=(const Device &other) = delete;
+ Device &operator=(Device &&other) = delete;
+
/**
* \brief Declaration of the virtual destructor
* \note resolve warning gcc 4.7
@@ -46,3 +56,5 @@ class Device {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:Device")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Enums.cc b/source/blender/compositor/intern/COM_Enums.cc
new file mode 100644
index 00000000000..d218de92544
--- /dev/null
+++ b/source/blender/compositor/intern/COM_Enums.cc
@@ -0,0 +1,61 @@
+/*
+ * 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_Enums.h"
+
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority)
+{
+ switch (priority) {
+ case eCompositorPriority::High: {
+ os << "Priority::High";
+ break;
+ }
+ case eCompositorPriority::Medium: {
+ os << "Priority::Medium";
+ break;
+ }
+ case eCompositorPriority::Low: {
+ os << "Priority::Low";
+ break;
+ }
+ }
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state)
+{
+ switch (execution_state) {
+ case eWorkPackageState::NotScheduled: {
+ os << "ExecutionState::NotScheduled";
+ break;
+ }
+ case eWorkPackageState::Scheduled: {
+ os << "ExecutionState::Scheduled";
+ break;
+ }
+ case eWorkPackageState::Executed: {
+ os << "ExecutionState::Executed";
+ break;
+ }
+ }
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h
new file mode 100644
index 00000000000..519e7df940e
--- /dev/null
+++ b/source/blender/compositor/intern/COM_Enums.h
@@ -0,0 +1,91 @@
+/*
+ * 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 "COM_defines.h"
+
+#include <ostream>
+
+namespace blender::compositor {
+
+/**
+ * \brief Possible quality settings
+ * \see CompositorContext.quality
+ * \ingroup Execution
+ */
+enum class eCompositorQuality {
+ /** \brief High quality setting */
+ High = 0,
+ /** \brief Medium quality setting */
+ Medium = 1,
+ /** \brief Low quality setting */
+ Low = 2,
+};
+
+/**
+ * \brief Possible priority settings
+ * \ingroup Execution
+ */
+enum class eCompositorPriority {
+ /** \brief High quality setting */
+ High = 2,
+ /** \brief Medium quality setting */
+ Medium = 1,
+ /** \brief Low quality setting */
+ Low = 0,
+};
+
+/**
+ * \brief the execution state of a chunk in an ExecutionGroup
+ * \ingroup Execution
+ */
+enum class eWorkPackageState {
+ /**
+ * \brief chunk is not yet scheduled
+ */
+ NotScheduled = 0,
+ /**
+ * \brief chunk is scheduled, but not yet executed
+ */
+ Scheduled = 1,
+ /**
+ * \brief chunk is executed.
+ */
+ Executed = 2,
+};
+
+/**
+ * \brief Work type to execute.
+ * \ingroup Execution
+ */
+enum class eWorkPackageType {
+ /**
+ * \brief Executes an execution group tile.
+ */
+ Tile = 0,
+ /**
+ * \brief Executes a custom function.
+ */
+ CustomFunction = 1
+};
+
+std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority);
+std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc
index f500327b7a7..68bda8c70d6 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.cc
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc
@@ -46,10 +46,31 @@
#include "WM_api.h"
#include "WM_types.h"
-ExecutionGroup::ExecutionGroup()
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags)
+{
+ if (flags.initialized) {
+ os << "init,";
+ }
+ if (flags.is_output) {
+ os << "output,";
+ }
+ if (flags.complex) {
+ os << "complex,";
+ }
+ if (flags.open_cl) {
+ os << "open_cl,";
+ }
+ if (flags.single_threaded) {
+ os << "single_threaded,";
+ }
+ return os;
+}
+
+ExecutionGroup::ExecutionGroup(int id)
{
- this->m_is_output = false;
- this->m_complex = false;
+ m_id = id;
this->m_bTree = nullptr;
this->m_height = 0;
this->m_width = 0;
@@ -57,42 +78,48 @@ ExecutionGroup::ExecutionGroup()
this->m_x_chunks_len = 0;
this->m_y_chunks_len = 0;
this->m_chunks_len = 0;
- this->m_initialized = false;
- this->m_openCL = false;
- this->m_singleThreaded = false;
this->m_chunks_finished = 0;
BLI_rcti_init(&this->m_viewerBorder, 0, 0, 0, 0);
this->m_executionStartTime = 0;
}
-CompositorPriority ExecutionGroup::getRenderPriority()
+std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group)
+{
+ os << "ExecutionGroup(id=" << execution_group.get_id();
+ os << ",flags={" << execution_group.get_flags() << "}";
+ os << ",operation=" << *execution_group.getOutputOperation() << "";
+ os << ")";
+ return os;
+}
+
+eCompositorPriority ExecutionGroup::getRenderPriority()
{
return this->getOutputOperation()->getRenderPriority();
}
bool ExecutionGroup::can_contain(NodeOperation &operation)
{
- if (!this->m_initialized) {
+ if (!m_flags.initialized) {
return true;
}
- if (operation.isReadBufferOperation()) {
+ if (operation.get_flags().is_read_buffer_operation) {
return true;
}
- if (operation.isWriteBufferOperation()) {
+ if (operation.get_flags().is_write_buffer_operation) {
return false;
}
- if (operation.isSetOperation()) {
+ if (operation.get_flags().is_set_operation) {
return true;
}
/* complex groups don't allow further ops (except read buffer and values, see above) */
- if (m_complex) {
+ if (m_flags.complex) {
return false;
}
/* complex ops can't be added to other groups (except their own, which they initialize, see
* above) */
- if (operation.isComplex()) {
+ if (operation.get_flags().complex) {
return false;
}
@@ -105,11 +132,12 @@ bool ExecutionGroup::addOperation(NodeOperation *operation)
return false;
}
- if (!operation->isReadBufferOperation() && !operation->isWriteBufferOperation()) {
- m_complex = operation->isComplex();
- m_openCL = operation->isOpenCL();
- m_singleThreaded = operation->isSingleThreaded();
- m_initialized = true;
+ if (!operation->get_flags().is_read_buffer_operation &&
+ !operation->get_flags().is_write_buffer_operation) {
+ m_flags.complex = operation->get_flags().complex;
+ m_flags.open_cl = operation->get_flags().open_cl;
+ m_flags.single_threaded = operation->get_flags().single_threaded;
+ m_flags.initialized = true;
}
m_operations.append(operation);
@@ -123,20 +151,26 @@ NodeOperation *ExecutionGroup::getOutputOperation() const
->m_operations[0]; /* the first operation of the group is always the output operation. */
}
-void ExecutionGroup::initExecution()
+void ExecutionGroup::init_work_packages()
{
- m_chunk_execution_states.clear();
- determineNumberOfChunks();
-
+ m_work_packages.clear();
if (this->m_chunks_len != 0) {
- m_chunk_execution_states.resize(this->m_chunks_len);
- m_chunk_execution_states.fill(eChunkExecutionState::NOT_SCHEDULED);
+ m_work_packages.resize(this->m_chunks_len);
+ for (unsigned int index = 0; index < m_chunks_len; index++) {
+ m_work_packages[index].type = eWorkPackageType::Tile;
+ m_work_packages[index].state = eWorkPackageState::NotScheduled;
+ m_work_packages[index].execution_group = this;
+ m_work_packages[index].chunk_number = index;
+ determineChunkRect(&m_work_packages[index].rect, index);
+ }
}
+}
+void ExecutionGroup::init_read_buffer_operations()
+{
unsigned int max_offset = 0;
-
for (NodeOperation *operation : m_operations) {
- if (operation->isReadBufferOperation()) {
+ if (operation->get_flags().is_read_buffer_operation) {
ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
this->m_read_operations.append(readOperation);
max_offset = MAX2(max_offset, readOperation->getOffset());
@@ -146,15 +180,23 @@ void ExecutionGroup::initExecution()
this->m_max_read_buffer_offset = max_offset;
}
+void ExecutionGroup::initExecution()
+{
+ init_number_of_chunks();
+ init_work_packages();
+ init_read_buffer_operations();
+}
+
void ExecutionGroup::deinitExecution()
{
- m_chunk_execution_states.clear();
+ m_work_packages.clear();
this->m_chunks_len = 0;
this->m_x_chunks_len = 0;
this->m_y_chunks_len = 0;
this->m_read_operations.clear();
this->m_bTree = nullptr;
}
+
void ExecutionGroup::determineResolution(unsigned int resolution[2])
{
NodeOperation *operation = this->getOutputOperation();
@@ -164,9 +206,9 @@ void ExecutionGroup::determineResolution(unsigned int resolution[2])
BLI_rcti_init(&this->m_viewerBorder, 0, this->m_width, 0, this->m_height);
}
-void ExecutionGroup::determineNumberOfChunks()
+void ExecutionGroup::init_number_of_chunks()
{
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
this->m_x_chunks_len = 1;
this->m_y_chunks_len = 1;
this->m_chunks_len = 1;
@@ -181,7 +223,7 @@ void ExecutionGroup::determineNumberOfChunks()
}
}
-blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() const
+blender::Array<unsigned int> ExecutionGroup::get_execution_order() const
{
blender::Array<unsigned int> chunk_order(m_chunks_len);
for (int chunk_index = 0; chunk_index < this->m_chunks_len; chunk_index++) {
@@ -193,7 +235,7 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
float centerY = 0.5f;
ChunkOrdering order_type = ChunkOrdering::Default;
- if (operation->isViewerOperation()) {
+ if (operation->get_flags().is_viewer_operation) {
ViewerOperation *viewer = (ViewerOperation *)operation;
centerX = viewer->getCenterX();
centerY = viewer->getCenterY();
@@ -216,11 +258,10 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
ChunkOrderHotspot hotspot(border_width * centerX, border_height * centerY, 0.0f);
blender::Array<ChunkOrder> chunk_orders(m_chunks_len);
for (index = 0; index < this->m_chunks_len; index++) {
- rcti rect;
- determineChunkRect(&rect, index);
+ const WorkPackage &work_package = m_work_packages[index];
chunk_orders[index].index = index;
- chunk_orders[index].x = rect.xmin - this->m_viewerBorder.xmin;
- chunk_orders[index].y = rect.ymin - this->m_viewerBorder.ymin;
+ chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin;
+ chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin;
chunk_orders[index].update_distance(&hotspot, 1);
}
@@ -254,11 +295,10 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
blender::Array<ChunkOrder> chunk_orders(m_chunks_len);
for (index = 0; index < this->m_chunks_len; index++) {
- rcti rect;
- determineChunkRect(&rect, index);
+ const WorkPackage &work_package = m_work_packages[index];
chunk_orders[index].index = index;
- chunk_orders[index].x = rect.xmin - this->m_viewerBorder.xmin;
- chunk_orders[index].y = rect.ymin - this->m_viewerBorder.ymin;
+ chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin;
+ chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin;
chunk_orders[index].update_distance(hotspots, 9);
}
@@ -301,7 +341,7 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
this->m_chunks_finished = 0;
this->m_bTree = bTree;
- blender::Array<unsigned int> chunk_order = determine_chunk_execution_order();
+ blender::Array<unsigned int> chunk_order = get_execution_order();
DebugInfo::execution_group_started(this);
DebugInfo::graphviz(graph);
@@ -322,8 +362,9 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
chunk_index = chunk_order[index];
int yChunk = chunk_index / this->m_x_chunks_len;
int xChunk = chunk_index - (yChunk * this->m_x_chunks_len);
- switch (m_chunk_execution_states[chunk_index]) {
- case eChunkExecutionState::NOT_SCHEDULED: {
+ const WorkPackage &work_package = m_work_packages[chunk_index];
+ switch (work_package.state) {
+ case eWorkPackageState::NotScheduled: {
scheduleChunkWhenPossible(graph, xChunk, yChunk);
finished = false;
startEvaluated = true;
@@ -334,13 +375,13 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
}
break;
}
- case eChunkExecutionState::SCHEDULED: {
+ case eWorkPackageState::Scheduled: {
finished = false;
startEvaluated = true;
numberEvaluated++;
break;
}
- case eChunkExecutionState::EXECUTED: {
+ case eWorkPackageState::Executed: {
if (!startEvaluated) {
startIndex = index + 1;
}
@@ -360,15 +401,14 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
{
- rcti rect;
- determineChunkRect(&rect, chunkNumber);
+ WorkPackage &work_package = m_work_packages[chunkNumber];
MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN(
sizeof(MemoryBuffer *) * this->m_max_read_buffer_offset, __func__);
rcti output;
for (ReadBufferOperation *readOperation : m_read_operations) {
MemoryProxy *memoryProxy = readOperation->getMemoryProxy();
- this->determineDependingAreaOfInterest(&rect, readOperation, &output);
+ this->determineDependingAreaOfInterest(&work_package.rect, readOperation, &output);
MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(
*memoryProxy, output);
memoryBuffers[readOperation->getOffset()] = memoryBuffer;
@@ -387,8 +427,9 @@ MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy &mem
void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers)
{
- if (this->m_chunk_execution_states[chunkNumber] == eChunkExecutionState::SCHEDULED) {
- this->m_chunk_execution_states[chunkNumber] = eChunkExecutionState::EXECUTED;
+ WorkPackage &work_package = m_work_packages[chunkNumber];
+ if (work_package.state == eWorkPackageState::Scheduled) {
+ work_package.state = eWorkPackageState::Executed;
}
atomic_add_and_fetch_u(&this->m_chunks_finished, 1);
@@ -420,23 +461,23 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo
}
}
-inline void ExecutionGroup::determineChunkRect(rcti *rect,
+inline void ExecutionGroup::determineChunkRect(rcti *r_rect,
const unsigned int xChunk,
const unsigned int yChunk) const
{
const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
BLI_rcti_init(
- rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
+ r_rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
}
else {
const unsigned int minx = xChunk * this->m_chunkSize + this->m_viewerBorder.xmin;
const unsigned int miny = yChunk * this->m_chunkSize + this->m_viewerBorder.ymin;
const unsigned int width = MIN2((unsigned int)this->m_viewerBorder.xmax, this->m_width);
const unsigned int height = MIN2((unsigned int)this->m_viewerBorder.ymax, this->m_height);
- BLI_rcti_init(rect,
+ BLI_rcti_init(r_rect,
MIN2(minx, this->m_width),
MIN2(minx + this->m_chunkSize, width),
MIN2(miny, this->m_height),
@@ -444,18 +485,18 @@ inline void ExecutionGroup::determineChunkRect(rcti *rect,
}
}
-void ExecutionGroup::determineChunkRect(rcti *rect, const unsigned int chunkNumber) const
+void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const
{
const unsigned int yChunk = chunkNumber / this->m_x_chunks_len;
const unsigned int xChunk = chunkNumber - (yChunk * this->m_x_chunks_len);
- determineChunkRect(rect, xChunk, yChunk);
+ determineChunkRect(r_rect, xChunk, yChunk);
}
MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
{
// we assume that this method is only called from complex execution groups.
NodeOperation *operation = this->getOutputOperation();
- if (operation->isWriteBufferOperation()) {
+ if (operation->get_flags().is_write_buffer_operation) {
WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation;
MemoryBuffer *buffer = new MemoryBuffer(
writeOperation->getMemoryProxy(), rect, MemoryBufferState::Temporary);
@@ -466,7 +507,7 @@ MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area)
{
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
return scheduleChunkWhenPossible(graph, 0, 0);
}
// find all chunks inside the rect
@@ -500,9 +541,10 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area
bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
{
- if (this->m_chunk_execution_states[chunkNumber] == eChunkExecutionState::NOT_SCHEDULED) {
- this->m_chunk_execution_states[chunkNumber] = eChunkExecutionState::SCHEDULED;
- WorkScheduler::schedule(this, chunkNumber);
+ WorkPackage &work_package = m_work_packages[chunkNumber];
+ if (work_package.state == eWorkPackageState::NotScheduled) {
+ work_package.state = eWorkPackageState::Scheduled;
+ WorkScheduler::schedule(&work_package);
return true;
}
return false;
@@ -521,22 +563,21 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph,
// Check if chunk is already executed or scheduled and not yet executed.
const int chunk_index = chunk_y * this->m_x_chunks_len + chunk_x;
- if (this->m_chunk_execution_states[chunk_index] == eChunkExecutionState::EXECUTED) {
+ WorkPackage &work_package = m_work_packages[chunk_index];
+ if (work_package.state == eWorkPackageState::Executed) {
return true;
}
- if (this->m_chunk_execution_states[chunk_index] == eChunkExecutionState::SCHEDULED) {
+ if (work_package.state == eWorkPackageState::Scheduled) {
return false;
}
- rcti rect;
- determineChunkRect(&rect, chunk_x, chunk_y);
bool can_be_executed = true;
rcti area;
for (ReadBufferOperation *read_operation : m_read_operations) {
BLI_rcti_init(&area, 0, 0, 0, 0);
MemoryProxy *memory_proxy = read_operation->getMemoryProxy();
- determineDependingAreaOfInterest(&rect, read_operation, &area);
+ determineDependingAreaOfInterest(&work_package.rect, read_operation, &area);
ExecutionGroup *group = memory_proxy->getExecutor();
if (!group->scheduleAreaWhenPossible(graph, &area)) {
@@ -558,16 +599,10 @@ void ExecutionGroup::determineDependingAreaOfInterest(rcti *input,
this->getOutputOperation()->determineDependingAreaOfInterest(input, readOperation, output);
}
-bool ExecutionGroup::isOpenCL()
-{
- return this->m_openCL;
-}
-
void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float ymax)
{
- NodeOperation *operation = this->getOutputOperation();
-
- if (operation->isViewerOperation() || operation->isPreviewOperation()) {
+ const NodeOperation &operation = *this->getOutputOperation();
+ if (operation.get_flags().use_viewer_border) {
BLI_rcti_init(&this->m_viewerBorder,
xmin * this->m_width,
xmax * this->m_width,
@@ -578,32 +613,14 @@ void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float y
void ExecutionGroup::setRenderBorder(float xmin, float xmax, float ymin, float ymax)
{
- NodeOperation *operation = this->getOutputOperation();
-
- if (operation->isOutputOperation(true)) {
- /* Basically, setting border need to happen for only operations
- * which operates in render resolution buffers (like compositor
- * output nodes).
- *
- * In this cases adding border will lead to mapping coordinates
- * from output buffer space to input buffer spaces when executing
- * operation.
- *
- * But nodes like viewer and file output just shall display or
- * safe the same exact buffer which goes to their input, no need
- * in any kind of coordinates mapping.
- */
-
- bool operationNeedsBorder = !(operation->isViewerOperation() ||
- operation->isPreviewOperation() ||
- operation->isFileOutputOperation());
-
- if (operationNeedsBorder) {
- BLI_rcti_init(&this->m_viewerBorder,
- xmin * this->m_width,
- xmax * this->m_width,
- ymin * this->m_height,
- ymax * this->m_height);
- }
+ const NodeOperation &operation = *this->getOutputOperation();
+ if (operation.isOutputOperation(true) && operation.get_flags().use_render_border) {
+ BLI_rcti_init(&this->m_viewerBorder,
+ xmin * this->m_width,
+ xmax * this->m_width,
+ ymin * this->m_height,
+ ymax * this->m_height);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h
index 13ff06cd5d1..cb593feabb0 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.h
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.h
@@ -31,32 +31,50 @@
#include "COM_MemoryProxy.h"
#include "COM_Node.h"
#include "COM_NodeOperation.h"
+#include "COM_WorkPackage.h"
#include <vector>
+namespace blender::compositor {
+
class ExecutionSystem;
class MemoryProxy;
+class MemoryBuffer;
class ReadBufferOperation;
class Device;
-/**
- * \brief the execution state of a chunk in an ExecutionGroup
- * \ingroup Execution
- */
-enum class eChunkExecutionState {
+struct ExecutionGroupFlags {
+ bool initialized : 1;
/**
- * \brief chunk is not yet scheduled
+ * Is this ExecutionGroup an output ExecutionGroup
+ * An OutputExecution group are groups containing a
+ * ViewerOperation, CompositeOperation, PreviewOperation.
*/
- NOT_SCHEDULED = 0,
+ bool is_output : 1;
+ bool complex : 1;
+
/**
- * \brief chunk is scheduled, but not yet executed
+ * Can this ExecutionGroup be scheduled on an OpenCLDevice.
*/
- SCHEDULED = 1,
+ bool open_cl : 1;
+
/**
- * \brief chunk is executed.
+ * Schedule this execution group as a single chunk. This
+ * chunk will be executed by a single thread.
*/
- EXECUTED = 2,
+ bool single_threaded : 1;
+
+ ExecutionGroupFlags()
+ {
+ initialized = false;
+ is_output = false;
+ complex = false;
+ open_cl = false;
+ single_threaded = false;
+ }
};
+std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags);
+
/**
* \brief Class ExecutionGroup is a group of Operations that are executed as one.
* This grouping is used to combine Operations that can be executed as one whole when
@@ -66,18 +84,17 @@ enum class eChunkExecutionState {
class ExecutionGroup {
private:
// fields
-
/**
- * \brief list of operations in this ExecutionGroup
+ * Id of the execution group. For debugging purposes.
*/
- blender::Vector<NodeOperation *> m_operations;
+ int m_id;
/**
- * \brief is this ExecutionGroup an input ExecutionGroup
- * an input execution group is a group that is at the end of the calculation
- * (the output is important for the user).
+ * \brief list of operations in this ExecutionGroup
*/
- bool m_is_output;
+ Vector<NodeOperation *> m_operations;
+
+ ExecutionGroupFlags m_flags;
/**
* \brief Width of the output
@@ -111,21 +128,6 @@ class ExecutionGroup {
unsigned int m_chunks_len;
/**
- * \brief contains this ExecutionGroup a complex NodeOperation.
- */
- bool m_complex;
-
- /**
- * \brief can this ExecutionGroup be scheduled on an OpenCLDevice
- */
- bool m_openCL;
-
- /**
- * \brief Is this Execution group SingleThreaded
- */
- bool m_singleThreaded;
-
- /**
* \brief what is the maximum number field of all ReadBufferOperation in this ExecutionGroup.
* \note this is used to construct the MemoryBuffers that will be passed during execution.
*/
@@ -134,7 +136,7 @@ class ExecutionGroup {
/**
* \brief All read operations of this execution group.
*/
- blender::Vector<ReadBufferOperation *> m_read_operations;
+ Vector<ReadBufferOperation *> m_read_operations;
/**
* \brief reference to the original bNodeTree,
@@ -149,24 +151,9 @@ class ExecutionGroup {
unsigned int m_chunks_finished;
/**
- * \brief m_chunk_execution_states holds per chunk the execution state. this state can be
- * - eChunkExecutionState::NOT_SCHEDULED: not scheduled
- * - eChunkExecutionState::SCHEDULED: scheduled
- * - eChunkExecutionState::EXECUTED: executed
- */
- blender::Vector<eChunkExecutionState> m_chunk_execution_states;
-
- /**
- * \brief indicator when this ExecutionGroup has valid Operations in its vector for Execution
- * \note When building the ExecutionGroup Operations are added via recursion.
- * First a WriteBufferOperations is added, then the.
- * \note Operation containing the settings that is important for the ExecutiongGroup is added,
- * \note When this occurs, these settings are copied over from the node to the ExecutionGroup
- * \note and the Initialized flag is set to true.
- * \see complex
- * \see openCL
+ * \brief m_work_packages holds all unit of work.
*/
- bool m_initialized;
+ Vector<WorkPackage> m_work_packages;
/**
* \brief denotes boundary for border compositing
@@ -187,25 +174,17 @@ class ExecutionGroup {
bool can_contain(NodeOperation &operation);
/**
- * \brief calculate the actual chunk size of this execution group.
- * \note A chunk size is an unsigned int that is both the height and width of a chunk.
- * \note The chunk size will not be stored in the chunkSize field. This needs to be done
- * \note by the calling method.
- */
- unsigned int determineChunkSize();
-
- /**
* \brief Determine the rect (minx, maxx, miny, maxy) of a chunk at a position.
- * \note Only gives useful results after the determination of the chunksize
- * \see determineChunkSize()
*/
- void determineChunkRect(rcti *rect, const unsigned int xChunk, const unsigned int yChunk) const;
+ void determineChunkRect(rcti *r_rect,
+ const unsigned int xChunk,
+ const unsigned int yChunk) const;
/**
* \brief determine the number of chunks, based on the chunkSize, width and height.
* \note The result are stored in the fields numberOfChunks, numberOfXChunks, numberOfYChunks
*/
- void determineNumberOfChunks();
+ void init_number_of_chunks();
/**
* \brief try to schedule a specific chunk.
@@ -252,11 +231,24 @@ class ExecutionGroup {
/**
* Return the execution order of the user visible chunks.
*/
- blender::Array<unsigned int> determine_chunk_execution_order() const;
+ blender::Array<unsigned int> get_execution_order() const;
+
+ void init_read_buffer_operations();
+ void init_work_packages();
public:
// constructors
- ExecutionGroup();
+ ExecutionGroup(int id);
+
+ int get_id() const
+ {
+ return m_id;
+ }
+
+ const ExecutionGroupFlags get_flags() const
+ {
+ return m_flags;
+ }
// methods
/**
@@ -270,23 +262,12 @@ class ExecutionGroup {
bool addOperation(NodeOperation *operation);
/**
- * \brief is this ExecutionGroup an output ExecutionGroup
- * \note An OutputExecution group are groups containing a
- * \note ViewerOperation, CompositeOperation, PreviewOperation.
- * \see NodeOperation.isOutputOperation
- */
- bool isOutputExecutionGroup() const
- {
- return this->m_is_output;
- }
-
- /**
* \brief set whether this ExecutionGroup is an output
* \param isOutput:
*/
void setOutputExecutionGroup(bool is_output)
{
- this->m_is_output = is_output;
+ this->m_flags.is_output = is_output;
}
/**
@@ -322,14 +303,6 @@ class ExecutionGroup {
}
/**
- * \brief does this ExecutionGroup contains a complex NodeOperation
- */
- bool isComplex() const
- {
- return m_complex;
- }
-
- /**
* \brief get the output operation of this ExecutionGroup
* \return NodeOperation *output operation
*/
@@ -404,16 +377,8 @@ class ExecutionGroup {
/**
* \brief Determine the rect (minx, maxx, miny, maxy) of a chunk.
- * \note Only gives useful results after the determination of the chunksize
- * \see determineChunkSize()
- */
- void determineChunkRect(rcti *rect, const unsigned int chunkNumber) const;
-
- /**
- * \brief can this ExecutionGroup be scheduled on an OpenCLDevice
- * \see WorkScheduler.schedule
*/
- bool isOpenCL();
+ void determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const;
void setChunksize(int chunksize)
{
@@ -424,7 +389,7 @@ class ExecutionGroup {
* \brief get the Render priority of this ExecutionGroup
* \see ExecutionSystem.execute
*/
- CompositorPriority getRenderPriority();
+ eCompositorPriority getRenderPriority();
/**
* \brief set border for viewer operation
@@ -441,3 +406,7 @@ class ExecutionGroup {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionGroup")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc
new file mode 100644
index 00000000000..4d7f62e091b
--- /dev/null
+++ b/source/blender/compositor/intern/COM_ExecutionModel.cc
@@ -0,0 +1,48 @@
+/*
+ * 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_ExecutionModel.h"
+
+namespace blender::compositor {
+
+ExecutionModel::ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations)
+ : context_(context), operations_(operations)
+{
+ const bNodeTree *node_tree = context_.getbNodeTree();
+
+ const rctf *viewer_border = &node_tree->viewer_border;
+ border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) &&
+ viewer_border->xmin < viewer_border->xmax &&
+ viewer_border->ymin < viewer_border->ymax;
+ border_.viewer_border = viewer_border;
+
+ const RenderData *rd = context_.getRenderData();
+ /* Case when cropping to render border happens is handled in
+ * compositor output and render layer nodes. */
+ border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) &&
+ !(rd->mode & R_CROP);
+ border_.render_border = &rd->border;
+}
+
+bool ExecutionModel::is_breaked() const
+{
+ const bNodeTree *btree = context_.getbNodeTree();
+ return btree->test_break(btree->tbh);
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h
new file mode 100644
index 00000000000..9e8466b9282
--- /dev/null
+++ b/source/blender/compositor/intern/COM_ExecutionModel.h
@@ -0,0 +1,84 @@
+/*
+ * 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 "BLI_rect.h"
+#include "BLI_vector.hh"
+
+#include "COM_ExecutionSystem.h"
+
+#include <functional>
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+class NodeOperation;
+
+/**
+ * Base class for execution models. Contains shared implementation.
+ */
+class ExecutionModel {
+ protected:
+ /**
+ * Render and viewer border info. Coordinates are normalized.
+ */
+ struct {
+ bool use_render_border;
+ const rctf *render_border;
+ bool use_viewer_border;
+ const rctf *viewer_border;
+ } border_;
+
+ /**
+ * Context used during execution.
+ */
+ CompositorContext &context_;
+
+ /**
+ * All operations being executed.
+ */
+ Span<NodeOperation *> operations_;
+
+ public:
+ ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations);
+
+ virtual ~ExecutionModel()
+ {
+ }
+
+ virtual void execute(ExecutionSystem &exec_system) = 0;
+
+ virtual void execute_work(const rcti &UNUSED(work_rect),
+ std::function<void(const rcti &split_rect)> UNUSED(work_func))
+ {
+ BLI_assert(!"Method not supported by current execution model");
+ }
+
+ protected:
+ bool is_breaked() const;
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc
index df97b8079b2..a12ec774032 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.cc
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc
@@ -21,22 +21,19 @@
#include "BLI_utildefines.h"
#include "PIL_time.h"
-#include "BKE_node.h"
-
-#include "BLT_translation.h"
-
-#include "COM_Converter.h"
#include "COM_Debug.h"
-#include "COM_ExecutionGroup.h"
+#include "COM_FullFrameExecutionModel.h"
#include "COM_NodeOperation.h"
#include "COM_NodeOperationBuilder.h"
-#include "COM_ReadBufferOperation.h"
+#include "COM_TiledExecutionModel.h"
#include "COM_WorkScheduler.h"
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
ExecutionSystem::ExecutionSystem(RenderData *rd,
Scene *scene,
bNodeTree *editingtree,
@@ -53,10 +50,10 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
this->m_context.setFastCalculation(fastcalculation);
/* initialize the CompositorContext */
if (rendering) {
- this->m_context.setQuality((CompositorQuality)editingtree->render_quality);
+ this->m_context.setQuality((eCompositorQuality)editingtree->render_quality);
}
else {
- this->m_context.setQuality((CompositorQuality)editingtree->edit_quality);
+ this->m_context.setQuality((eCompositorQuality)editingtree->edit_quality);
}
this->m_context.setRendering(rendering);
this->m_context.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() &&
@@ -71,41 +68,23 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
builder.convertToOperations(this);
}
- unsigned int resolution[2];
-
- rctf *viewer_border = &editingtree->viewer_border;
- bool use_viewer_border = (editingtree->flag & NTREE_VIEWER_BORDER) &&
- viewer_border->xmin < viewer_border->xmax &&
- viewer_border->ymin < viewer_border->ymax;
-
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Determining resolution"));
-
- for (ExecutionGroup *executionGroup : m_groups) {
- resolution[0] = 0;
- resolution[1] = 0;
- executionGroup->determineResolution(resolution);
-
- if (rendering) {
- /* case when cropping to render border happens is handled in
- * compositor output and render layer nodes
- */
- if ((rd->mode & R_BORDER) && !(rd->mode & R_CROP)) {
- executionGroup->setRenderBorder(
- rd->border.xmin, rd->border.xmax, rd->border.ymin, rd->border.ymax);
- }
- }
-
- if (use_viewer_border) {
- executionGroup->setViewerBorder(
- viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax);
- }
+ switch (m_context.get_execution_model()) {
+ case eExecutionModel::Tiled:
+ execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups);
+ break;
+ case eExecutionModel::FullFrame:
+ execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations);
+ break;
+ default:
+ BLI_assert(!"Non implemented execution model");
+ break;
}
-
- // DebugInfo::graphviz(this);
}
ExecutionSystem::~ExecutionSystem()
{
+ delete execution_model_;
+
for (NodeOperation *operation : m_operations) {
delete operation;
}
@@ -117,105 +96,23 @@ ExecutionSystem::~ExecutionSystem()
this->m_groups.clear();
}
-void ExecutionSystem::set_operations(const blender::Vector<NodeOperation *> &operations,
- const blender::Vector<ExecutionGroup *> &groups)
+void ExecutionSystem::set_operations(const Vector<NodeOperation *> &operations,
+ const Vector<ExecutionGroup *> &groups)
{
m_operations = operations;
m_groups = groups;
}
-static void update_read_buffer_offset(blender::Vector<NodeOperation *> &operations)
-{
- unsigned int order = 0;
- for (NodeOperation *operation : operations) {
- if (operation->isReadBufferOperation()) {
- ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
- readOperation->setOffset(order);
- order++;
- }
- }
-}
-
-static void init_write_operations_for_execution(blender::Vector<NodeOperation *> &operations,
- const bNodeTree *bTree)
-{
- for (NodeOperation *operation : operations) {
- if (operation->isWriteBufferOperation()) {
- operation->setbNodeTree(bTree);
- operation->initExecution();
- }
- }
-}
-
-static void link_write_buffers(blender::Vector<NodeOperation *> &operations)
-{
- for (NodeOperation *operation : operations) {
- if (operation->isReadBufferOperation()) {
- ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
- readOperation->updateMemoryBuffer();
- }
- }
-}
-
-static void init_non_write_operations_for_execution(blender::Vector<NodeOperation *> &operations,
- const bNodeTree *bTree)
-{
- for (NodeOperation *operation : operations) {
- if (!operation->isWriteBufferOperation()) {
- operation->setbNodeTree(bTree);
- operation->initExecution();
- }
- }
-}
-
-static void init_execution_groups_for_execution(blender::Vector<ExecutionGroup *> &groups,
- const int chunk_size)
-{
- for (ExecutionGroup *execution_group : groups) {
- execution_group->setChunksize(chunk_size);
- execution_group->initExecution();
- }
-}
-
void ExecutionSystem::execute()
{
- const bNodeTree *editingtree = this->m_context.getbNodeTree();
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution"));
-
DebugInfo::execute_started(this);
- update_read_buffer_offset(m_operations);
-
- init_write_operations_for_execution(m_operations, m_context.getbNodeTree());
- link_write_buffers(m_operations);
- init_non_write_operations_for_execution(m_operations, m_context.getbNodeTree());
- init_execution_groups_for_execution(m_groups, m_context.getChunksize());
-
- WorkScheduler::start(this->m_context);
- execute_groups(CompositorPriority::High);
- if (!this->getContext().isFastCalculation()) {
- execute_groups(CompositorPriority::Medium);
- execute_groups(CompositorPriority::Low);
- }
- WorkScheduler::finish();
- WorkScheduler::stop();
-
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution"));
-
- for (NodeOperation *operation : m_operations) {
- operation->deinitExecution();
- }
-
- for (ExecutionGroup *execution_group : m_groups) {
- execution_group->deinitExecution();
- }
+ execution_model_->execute(*this);
}
-void ExecutionSystem::execute_groups(CompositorPriority priority)
+void ExecutionSystem::execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func)
{
- for (ExecutionGroup *execution_group : m_groups) {
- if (execution_group->isOutputExecutionGroup() &&
- execution_group->getRenderPriority() == priority) {
- execution_group->execute(this);
- }
- }
+ execution_model_->execute_work(work_rect, work_func);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h
index c12380fe839..e106209651c 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.h
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.h
@@ -25,12 +25,15 @@ class ExecutionGroup;
#include "COM_ExecutionGroup.h"
#include "COM_Node.h"
#include "COM_NodeOperation.h"
+#include "COM_SharedOperationBuffers.h"
#include "DNA_color_types.h"
#include "DNA_node_types.h"
#include "BLI_vector.hh"
+namespace blender::compositor {
+
/**
* \page execution Execution model
* In order to get to an efficient model for execution, several steps are being done. these steps
@@ -70,17 +73,17 @@ class ExecutionGroup;
*
* - Image size conversions: the system can automatically convert when resolutions do not match.
* An NodeInput has a resize mode. This can be any of the following settings.
- * - [@ref InputSocketResizeMode.COM_SC_CENTER]:
+ * - [@ref InputSocketResizeMode.ResizeMode::Center]:
* The center of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT_WIDTH]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitWidth]:
* The width of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT_HEIGHT]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitHeight]:
* The height of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitAny]:
* The width, or the height of both images are aligned to make sure that it fits.
- * - [@ref InputSocketResizeMode.COM_SC_STRETCH]:
+ * - [@ref InputSocketResizeMode.ResizeMode::Stretch]:
* The width and the height of both images are aligned.
- * - [@ref InputSocketResizeMode.COM_SC_NO_RESIZE]:
+ * - [@ref InputSocketResizeMode.ResizeMode::None]:
* Bottom left of the images are aligned.
*
* \see COM_convert_data_type Datatype conversions
@@ -113,13 +116,21 @@ class ExecutionGroup;
* \see ExecutionGroup class representing the ExecutionGroup
*/
+/* Forward declarations. */
+class ExecutionModel;
+
/**
* \brief the ExecutionSystem contains the whole compositor tree.
*/
class ExecutionSystem {
-
private:
/**
+ * Contains operations active buffers data. Buffers will be disposed once reader operations are
+ * finished.
+ */
+ SharedOperationBuffers active_buffers_;
+
+ /**
* \brief the context used during execution
*/
CompositorContext m_context;
@@ -127,12 +138,17 @@ class ExecutionSystem {
/**
* \brief vector of operations
*/
- blender::Vector<NodeOperation *> m_operations;
+ Vector<NodeOperation *> m_operations;
/**
* \brief vector of groups
*/
- blender::Vector<ExecutionGroup *> m_groups;
+ Vector<ExecutionGroup *> m_groups;
+
+ /**
+ * Active execution model implementation.
+ */
+ ExecutionModel *execution_model_;
private: // methods
public:
@@ -157,8 +173,8 @@ class ExecutionSystem {
*/
~ExecutionSystem();
- void set_operations(const blender::Vector<NodeOperation *> &operations,
- const blender::Vector<ExecutionGroup *> &groups);
+ void set_operations(const Vector<NodeOperation *> &operations,
+ const Vector<ExecutionGroup *> &groups);
/**
* \brief execute this system
@@ -176,9 +192,14 @@ class ExecutionSystem {
return this->m_context;
}
- private:
- void execute_groups(CompositorPriority priority);
+ SharedOperationBuffers &get_active_buffers()
+ {
+ return active_buffers_;
+ }
+ void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func);
+
+ private:
/* allow the DebugInfo class to look at internals */
friend class DebugInfo;
@@ -186,3 +207,5 @@ class ExecutionSystem {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionSystem")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
new file mode 100644
index 00000000000..21075bb7255
--- /dev/null
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -0,0 +1,362 @@
+/*
+ * 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_FullFrameExecutionModel.h"
+#include "COM_Debug.h"
+#include "COM_ExecutionGroup.h"
+#include "COM_ReadBufferOperation.h"
+#include "COM_WorkScheduler.h"
+
+#include "BLT_translation.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context,
+ SharedOperationBuffers &shared_buffers,
+ Span<NodeOperation *> operations)
+ : ExecutionModel(context, operations),
+ active_buffers_(shared_buffers),
+ num_operations_finished_(0),
+ work_mutex_(),
+ work_finished_cond_()
+{
+ priorities_.append(eCompositorPriority::High);
+ if (!context.isFastCalculation()) {
+ priorities_.append(eCompositorPriority::Medium);
+ priorities_.append(eCompositorPriority::Low);
+ }
+
+ BLI_mutex_init(&work_mutex_);
+ BLI_condition_init(&work_finished_cond_);
+}
+
+FullFrameExecutionModel::~FullFrameExecutionModel()
+{
+ BLI_condition_end(&work_finished_cond_);
+ BLI_mutex_end(&work_mutex_);
+}
+
+void FullFrameExecutionModel::execute(ExecutionSystem &exec_system)
+{
+ const bNodeTree *node_tree = this->context_.getbNodeTree();
+ node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution"));
+
+ DebugInfo::graphviz(&exec_system);
+
+ determine_areas_to_render_and_reads();
+ render_operations(exec_system);
+}
+
+void FullFrameExecutionModel::determine_areas_to_render_and_reads()
+{
+ const bool is_rendering = context_.isRendering();
+ const bNodeTree *node_tree = context_.getbNodeTree();
+
+ rcti area;
+ for (eCompositorPriority priority : priorities_) {
+ for (NodeOperation *op : operations_) {
+ op->setbNodeTree(node_tree);
+ if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) {
+ get_output_render_area(op, area);
+ determine_areas_to_render(op, area);
+ determine_reads(op);
+ }
+ }
+ }
+}
+
+Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op)
+{
+ const int num_inputs = op->getNumberOfInputSockets();
+ Vector<MemoryBuffer *> inputs_buffers(num_inputs);
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = op->get_input_operation(i);
+ inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op);
+ }
+ return inputs_buffers;
+}
+
+MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op)
+{
+ rcti op_rect;
+ BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight());
+
+ const DataType data_type = op->getOutputSocket(0)->getDataType();
+ /* TODO: We should check if the operation is constant instead of is_set_operation. Finding a way
+ * to know if an operation is constant has to be implemented yet. */
+ const bool is_a_single_elem = op->get_flags().is_set_operation;
+ return new MemoryBuffer(data_type, op_rect, is_a_single_elem);
+}
+
+void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSystem &exec_system)
+{
+ Vector<MemoryBuffer *> input_bufs = get_input_buffers(op);
+
+ const bool has_outputs = op->getNumberOfOutputSockets() > 0;
+ MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr;
+ Span<rcti> areas = active_buffers_.get_areas_to_render(op);
+ op->render(op_buf, areas, input_bufs, exec_system);
+ active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf));
+
+ operation_finished(op);
+}
+
+/**
+ * Render output operations in order of priority.
+ */
+void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system)
+{
+ const bool is_rendering = context_.isRendering();
+
+ WorkScheduler::start(this->context_);
+ for (eCompositorPriority priority : priorities_) {
+ for (NodeOperation *op : operations_) {
+ if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) {
+ render_output_dependencies(op, exec_system);
+ render_operation(op, exec_system);
+ }
+ }
+ }
+ WorkScheduler::stop();
+}
+
+/**
+ * Returns all dependencies from inputs to outputs. A dependency may be repeated when
+ * several operations depend on it.
+ */
+static Vector<NodeOperation *> get_operation_dependencies(NodeOperation *operation)
+{
+ /* Get dependencies from outputs to inputs. */
+ Vector<NodeOperation *> dependencies;
+ Vector<NodeOperation *> next_outputs;
+ next_outputs.append(operation);
+ while (next_outputs.size() > 0) {
+ Vector<NodeOperation *> outputs(next_outputs);
+ next_outputs.clear();
+ for (NodeOperation *output : outputs) {
+ for (int i = 0; i < output->getNumberOfInputSockets(); i++) {
+ next_outputs.append(output->get_input_operation(i));
+ }
+ }
+ dependencies.extend(next_outputs);
+ }
+
+ /* Reverse to get dependencies from inputs to outputs. */
+ std::reverse(dependencies.begin(), dependencies.end());
+
+ return dependencies;
+}
+
+void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_op,
+ ExecutionSystem &exec_system)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+ Vector<NodeOperation *> dependencies = get_operation_dependencies(output_op);
+ for (NodeOperation *op : dependencies) {
+ if (!active_buffers_.is_operation_rendered(op)) {
+ render_operation(op, exec_system);
+ }
+ }
+}
+
+/**
+ * Determines all operations areas needed to render given output area.
+ */
+void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op,
+ const rcti &output_area)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ Vector<std::pair<NodeOperation *, const rcti>> stack;
+ stack.append({output_op, output_area});
+ while (stack.size() > 0) {
+ std::pair<NodeOperation *, rcti> pair = stack.pop_last();
+ NodeOperation *operation = pair.first;
+ const rcti &render_area = pair.second;
+ if (active_buffers_.is_area_registered(operation, render_area)) {
+ continue;
+ }
+
+ active_buffers_.register_area(operation, render_area);
+
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ rcti input_op_rect, input_area;
+ BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight());
+ operation->get_area_of_interest(input_op, render_area, input_area);
+
+ /* Ensure area of interest is within operation bounds, cropping areas outside. */
+ BLI_rcti_isect(&input_area, &input_op_rect, &input_area);
+
+ stack.append({input_op, input_area});
+ }
+ }
+}
+
+/**
+ * Determines reads to receive by operations in output operation tree (i.e: Number of dependent
+ * operations each operation has).
+ */
+void FullFrameExecutionModel::determine_reads(NodeOperation *output_op)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ Vector<NodeOperation *> stack;
+ stack.append(output_op);
+ while (stack.size() > 0) {
+ NodeOperation *operation = stack.pop_last();
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ if (!active_buffers_.has_registered_reads(input_op)) {
+ stack.append(input_op);
+ }
+ active_buffers_.register_read(input_op);
+ }
+ }
+}
+
+/**
+ * Calculates given output operation area to be rendered taking into account viewer and render
+ * borders.
+ */
+void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ /* By default return operation bounds (no border). */
+ const int op_width = output_op->getWidth();
+ const int op_height = output_op->getHeight();
+ BLI_rcti_init(&r_area, 0, op_width, 0, op_height);
+
+ const bool has_viewer_border = border_.use_viewer_border &&
+ (output_op->get_flags().is_viewer_operation ||
+ output_op->get_flags().is_preview_operation);
+ const bool has_render_border = border_.use_render_border;
+ if (has_viewer_border || has_render_border) {
+ /* Get border with normalized coordinates. */
+ const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border;
+
+ /* Return de-normalized border. */
+ BLI_rcti_init(&r_area,
+ norm_border->xmin * op_width,
+ norm_border->xmax * op_width,
+ norm_border->ymin * op_height,
+ norm_border->ymax * op_height);
+ }
+}
+
+/**
+ * Multi-threadedly execute given work function passing work_rect splits as argument.
+ */
+void FullFrameExecutionModel::execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func)
+{
+ if (is_breaked()) {
+ return;
+ }
+
+ /* Split work vertically to maximize continuous memory. */
+ const int work_height = BLI_rcti_size_y(&work_rect);
+ const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height);
+ const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works;
+ int remaining_height = work_height - split_height * num_sub_works;
+
+ Vector<WorkPackage> sub_works(num_sub_works);
+ int sub_work_y = work_rect.ymin;
+ int num_sub_works_finished = 0;
+ for (int i = 0; i < num_sub_works; i++) {
+ int sub_work_height = split_height;
+
+ /* Distribute remaining height between sub-works. */
+ if (remaining_height > 0) {
+ sub_work_height++;
+ remaining_height--;
+ }
+
+ WorkPackage &sub_work = sub_works[i];
+ sub_work.type = eWorkPackageType::CustomFunction;
+ sub_work.execute_fn = [=, &work_func, &work_rect]() {
+ if (is_breaked()) {
+ return;
+ }
+ rcti split_rect;
+ BLI_rcti_init(
+ &split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height);
+ work_func(split_rect);
+ };
+ sub_work.executed_fn = [&]() {
+ BLI_mutex_lock(&work_mutex_);
+ num_sub_works_finished++;
+ if (num_sub_works_finished == num_sub_works) {
+ BLI_condition_notify_one(&work_finished_cond_);
+ }
+ BLI_mutex_unlock(&work_mutex_);
+ };
+ WorkScheduler::schedule(&sub_work);
+ sub_work_y += sub_work_height;
+ }
+ BLI_assert(sub_work_y == work_rect.ymax);
+
+ WorkScheduler::finish();
+
+ /* Ensure all sub-works finished.
+ * TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading
+ * model. Sync code should be removed once it's fixed. */
+ BLI_mutex_lock(&work_mutex_);
+ if (num_sub_works_finished < num_sub_works) {
+ BLI_condition_wait(&work_finished_cond_, &work_mutex_);
+ }
+ BLI_mutex_unlock(&work_mutex_);
+}
+
+void FullFrameExecutionModel::operation_finished(NodeOperation *operation)
+{
+ /* Report inputs reads so that buffers may be freed/reused. */
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ active_buffers_.read_finished(operation->get_input_operation(i));
+ }
+
+ num_operations_finished_++;
+ update_progress_bar();
+}
+
+void FullFrameExecutionModel::update_progress_bar()
+{
+ const bNodeTree *tree = context_.getbNodeTree();
+ if (tree) {
+ const float progress = num_operations_finished_ / static_cast<float>(operations_.size());
+ tree->progress(tree->prh, progress);
+
+ char buf[128];
+ BLI_snprintf(buf,
+ sizeof(buf),
+ TIP_("Compositing | Operation %i-%li"),
+ num_operations_finished_ + 1,
+ operations_.size());
+ tree->stats_draw(tree->sdh, buf);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
new file mode 100644
index 00000000000..e68ad93b407
--- /dev/null
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
@@ -0,0 +1,88 @@
+/*
+ * 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 "COM_ExecutionModel.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+class ExecutionGroup;
+
+/**
+ * Fully renders operations in order from inputs to outputs.
+ */
+class FullFrameExecutionModel : public ExecutionModel {
+ private:
+ /**
+ * Contains operations active buffers data. Buffers will be disposed once reader operations are
+ * finished.
+ */
+ SharedOperationBuffers &active_buffers_;
+
+ /**
+ * Number of operations finished.
+ */
+ int num_operations_finished_;
+
+ /**
+ * Order of priorities for output operations execution.
+ */
+ Vector<eCompositorPriority> priorities_;
+
+ ThreadMutex work_mutex_;
+ ThreadCondition work_finished_cond_;
+
+ public:
+ FullFrameExecutionModel(CompositorContext &context,
+ SharedOperationBuffers &shared_buffers,
+ Span<NodeOperation *> operations);
+ ~FullFrameExecutionModel();
+
+ void execute(ExecutionSystem &exec_system) override;
+
+ void execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func) override;
+
+ private:
+ void determine_areas_to_render_and_reads();
+ void render_operations(ExecutionSystem &exec_system);
+ void render_output_dependencies(NodeOperation *output_op, ExecutionSystem &exec_system);
+ Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op);
+ MemoryBuffer *create_operation_buffer(NodeOperation *op);
+ void render_operation(NodeOperation *op, ExecutionSystem &exec_system);
+
+ void operation_finished(NodeOperation *operation);
+
+ void get_output_render_area(NodeOperation *output_op, rcti &r_area);
+ void determine_areas_to_render(NodeOperation *output_op, const rcti &output_area);
+ void determine_reads(NodeOperation *output_op);
+
+ void update_progress_bar();
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:FullFrameExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index 0b28168720e..8c30d3215d7 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -20,45 +20,54 @@
#include "MEM_guardedalloc.h"
-static unsigned int determine_num_channels(DataType datatype)
-{
- switch (datatype) {
- case DataType::Value:
- return COM_NUM_CHANNELS_VALUE;
- case DataType::Vector:
- return COM_NUM_CHANNELS_VECTOR;
- case DataType::Color:
- default:
- return COM_NUM_CHANNELS_COLOR;
- }
-}
+namespace blender::compositor {
MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state)
{
m_rect = rect;
+ this->m_is_a_single_elem = false;
this->m_memoryProxy = memoryProxy;
- this->m_num_channels = determine_num_channels(memoryProxy->getDataType());
+ this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType());
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = state;
this->m_datatype = memoryProxy->getDataType();
+
+ set_strides();
}
-MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect)
+MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem)
{
m_rect = rect;
+ this->m_is_a_single_elem = is_a_single_elem;
this->m_memoryProxy = nullptr;
- this->m_num_channels = determine_num_channels(dataType);
+ this->m_num_channels = COM_data_type_num_channels(dataType);
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = MemoryBufferState::Temporary;
this->m_datatype = dataType;
+
+ set_strides();
}
MemoryBuffer::MemoryBuffer(const MemoryBuffer &src)
- : MemoryBuffer(src.m_memoryProxy, src.m_rect, MemoryBufferState::Temporary)
+ : MemoryBuffer(src.m_datatype, src.m_rect, false)
+{
+ m_memoryProxy = src.m_memoryProxy;
+ /* src may be single elem buffer */
+ fill_from(src);
+}
+
+void MemoryBuffer::set_strides()
{
- memcpy(m_buffer, src.m_buffer, buffer_len() * m_num_channels * sizeof(float));
+ if (m_is_a_single_elem) {
+ this->elem_stride = 0;
+ this->row_stride = 0;
+ }
+ else {
+ this->elem_stride = m_num_channels;
+ this->row_stride = getWidth() * m_num_channels;
+ }
}
void MemoryBuffer::clear()
@@ -111,6 +120,8 @@ MemoryBuffer::~MemoryBuffer()
void MemoryBuffer::fill_from(const MemoryBuffer &src)
{
+ BLI_assert(!this->is_a_single_elem());
+
unsigned int otherY;
unsigned int minX = MAX2(this->m_rect.xmin, src.m_rect.xmin);
unsigned int maxX = MIN2(this->m_rect.xmax, src.m_rect.xmax);
@@ -120,10 +131,8 @@ void MemoryBuffer::fill_from(const MemoryBuffer &src)
int otherOffset;
for (otherY = minY; otherY < maxY; otherY++) {
- otherOffset = ((otherY - src.m_rect.ymin) * src.getWidth() + minX - src.m_rect.xmin) *
- this->m_num_channels;
- offset = ((otherY - this->m_rect.ymin) * getWidth() + minX - this->m_rect.xmin) *
- this->m_num_channels;
+ otherOffset = src.get_coords_offset(minX, otherY);
+ offset = this->get_coords_offset(minX, otherY);
memcpy(&this->m_buffer[offset],
&src.m_buffer[otherOffset],
(maxX - minX) * this->m_num_channels * sizeof(float));
@@ -134,8 +143,7 @@ void MemoryBuffer::writePixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
- const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
- this->m_num_channels;
+ const int offset = get_coords_offset(x, y);
memcpy(&this->m_buffer[offset], color, sizeof(float) * this->m_num_channels);
}
}
@@ -144,8 +152,7 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
- const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
- this->m_num_channels;
+ const int offset = get_coords_offset(x, y);
float *dst = &this->m_buffer[offset];
const float *src = color;
for (int i = 0; i < this->m_num_channels; i++, dst++, src++) {
@@ -162,24 +169,31 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4]
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
{
- BLI_assert(this->m_datatype == DataType::Color);
- float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
- /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
- * but compositor uses pixel space. For now let's just divide the values and
- * switch compositor to normalized space for EWA later.
- */
- float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height};
- float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height};
- float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height};
-
- BLI_ewa_filter(this->getWidth(),
- this->getHeight(),
- false,
- true,
- uv_normal,
- du_normal,
- dv_normal,
- read_ewa_pixel_sampled,
- this,
- result);
+ if (m_is_a_single_elem) {
+ memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
+ }
+ else {
+ BLI_assert(this->m_datatype == DataType::Color);
+ float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
+ /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
+ * but compositor uses pixel space. For now let's just divide the values and
+ * switch compositor to normalized space for EWA later.
+ */
+ float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height};
+ float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height};
+ float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height};
+
+ BLI_ewa_filter(this->getWidth(),
+ this->getHeight(),
+ false,
+ true,
+ uv_normal,
+ du_normal,
+ dv_normal,
+ read_ewa_pixel_sampled,
+ this,
+ result);
+ }
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index 6f719b61122..97b220508e0 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -16,17 +16,16 @@
* Copyright 2011, Blender Foundation.
*/
-class MemoryBuffer;
-
#pragma once
#include "COM_ExecutionGroup.h"
#include "COM_MemoryProxy.h"
-#include "COM_SocketReader.h"
#include "BLI_math.h"
#include "BLI_rect.h"
+namespace blender::compositor {
+
/**
* \brief state of a memory buffer
* \ingroup Memory
@@ -51,6 +50,25 @@ class MemoryProxy;
* \brief a MemoryBuffer contains access to the data of a chunk
*/
class MemoryBuffer {
+ public:
+ /**
+ * Offset between elements.
+ *
+ * Should always be used for the x dimension when calculating buffer offsets.
+ * It will be 0 when is_a_single_elem=true.
+ * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
+ */
+ int elem_stride;
+
+ /**
+ * Offset between rows.
+ *
+ * Should always be used for the y dimension when calculating buffer offsets.
+ * It will be 0 when is_a_single_elem=true.
+ * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
+ */
+ int row_stride;
+
private:
/**
* \brief proxy of the memory (same for all chunks in the same buffer)
@@ -83,6 +101,11 @@ class MemoryBuffer {
*/
uint8_t m_num_channels;
+ /**
+ * Whether buffer is a single element in memory.
+ */
+ bool m_is_a_single_elem;
+
public:
/**
* \brief construct new temporarily MemoryBuffer for an area
@@ -92,7 +115,7 @@ class MemoryBuffer {
/**
* \brief construct new temporarily MemoryBuffer for an area
*/
- MemoryBuffer(DataType datatype, const rcti &rect);
+ MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false);
/**
* Copy constructor
@@ -104,6 +127,102 @@ class MemoryBuffer {
*/
~MemoryBuffer();
+ /**
+ * Whether buffer is a single element in memory independently of its resolution. True for set
+ * operations buffers.
+ */
+ bool is_a_single_elem() const
+ {
+ return m_is_a_single_elem;
+ }
+
+ float &operator[](int index)
+ {
+ BLI_assert(m_is_a_single_elem ? index < m_num_channels :
+ index < get_coords_offset(getWidth(), getHeight()));
+ return m_buffer[index];
+ }
+
+ const float &operator[](int index) const
+ {
+ BLI_assert(m_is_a_single_elem ? index < m_num_channels :
+ index < get_coords_offset(getWidth(), getHeight()));
+ return m_buffer[index];
+ }
+
+ /**
+ * Get offset needed to jump from buffer start to given coordinates.
+ */
+ int get_coords_offset(int x, int y) const
+ {
+ return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride;
+ }
+
+ /**
+ * Get buffer element at given coordinates.
+ */
+ float *get_elem(int x, int y)
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ return m_buffer + get_coords_offset(x, y);
+ }
+
+ /**
+ * Get buffer element at given coordinates.
+ */
+ const float *get_elem(int x, int y) const
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ return m_buffer + get_coords_offset(x, y);
+ }
+
+ /**
+ * Get channel value at given coordinates.
+ */
+ float &get_value(int x, int y, int channel)
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
+ channel >= 0 && channel < m_num_channels);
+ return m_buffer[get_coords_offset(x, y) + channel];
+ }
+
+ /**
+ * Get channel value at given coordinates.
+ */
+ const float &get_value(int x, int y, int channel) const
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
+ channel >= 0 && channel < m_num_channels);
+ return m_buffer[get_coords_offset(x, y) + channel];
+ }
+
+ /**
+ * Get the buffer row end.
+ */
+ const float *get_row_end(int y) const
+ {
+ BLI_assert(y >= 0 && y < getHeight());
+ return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
+ }
+
+ /**
+ * Get the number of elements in memory for a row. For single element buffers it will always
+ * be 1.
+ */
+ int get_memory_width() const
+ {
+ return is_a_single_elem() ? 1 : getWidth();
+ }
+
+ /**
+ * Get number of elements in memory for a column. For single element buffers it will
+ * always be 1.
+ */
+ int get_memory_height() const
+ {
+ return is_a_single_elem() ? 1 : getHeight();
+ }
+
uint8_t get_num_channels()
{
return this->m_num_channels;
@@ -217,7 +336,7 @@ class MemoryBuffer {
int u = x;
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
- const int offset = (getWidth() * y + x) * this->m_num_channels;
+ const int offset = get_coords_offset(u, v);
float *buffer = &this->m_buffer[offset];
memcpy(result, buffer, sizeof(float) * this->m_num_channels);
}
@@ -233,7 +352,7 @@ class MemoryBuffer {
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
- const int offset = (getWidth() * v + u) * this->m_num_channels;
+ const int offset = get_coords_offset(u, v);
BLI_assert(offset >= 0);
BLI_assert(offset < this->buffer_len() * this->m_num_channels);
@@ -259,15 +378,20 @@ class MemoryBuffer {
copy_vn_fl(result, this->m_num_channels, 0.0f);
return;
}
- BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
- result,
- getWidth(),
- getHeight(),
- this->m_num_channels,
- u,
- v,
- extend_x == MemoryBufferExtend::Repeat,
- extend_y == MemoryBufferExtend::Repeat);
+ if (m_is_a_single_elem) {
+ memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
+ }
+ else {
+ BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
+ result,
+ getWidth(),
+ getHeight(),
+ this->m_num_channels,
+ u,
+ v,
+ extend_x == MemoryBufferExtend::Repeat,
+ extend_y == MemoryBufferExtend::Repeat);
+ }
}
void readEWA(float *result, const float uv[2], const float derivatives[2][2]);
@@ -322,12 +446,15 @@ class MemoryBuffer {
float get_max_value(const rcti &rect) const;
private:
+ void set_strides();
const int buffer_len() const
{
- return getWidth() * getHeight();
+ return get_memory_width() * get_memory_height();
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cc b/source/blender/compositor/intern/COM_MemoryProxy.cc
index 8ef834e1efe..6023850c944 100644
--- a/source/blender/compositor/intern/COM_MemoryProxy.cc
+++ b/source/blender/compositor/intern/COM_MemoryProxy.cc
@@ -18,6 +18,12 @@
#include "COM_MemoryProxy.h"
+#include "COM_MemoryBuffer.h"
+
+#include "BLI_rect.h"
+
+namespace blender::compositor {
+
MemoryProxy::MemoryProxy(DataType datatype)
{
this->m_writeBufferOperation = nullptr;
@@ -43,3 +49,5 @@ void MemoryProxy::free()
this->m_buffer = nullptr;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h
index ee98ff41630..6814afada74 100644
--- a/source/blender/compositor/intern/COM_MemoryProxy.h
+++ b/source/blender/compositor/intern/COM_MemoryProxy.h
@@ -16,13 +16,18 @@
* Copyright 2011, Blender Foundation.
*/
-class MemoryProxy;
-
#pragma once
-#include "COM_ExecutionGroup.h"
-#include "COM_MemoryBuffer.h"
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+#include "COM_defines.h"
+
+namespace blender::compositor {
+/* Forward declarations. */
+class MemoryBuffer;
class ExecutionGroup;
class WriteBufferOperation;
@@ -69,7 +74,7 @@ class MemoryProxy {
/**
* \brief get the ExecutionGroup that can be scheduled to calculate a certain chunk.
*/
- ExecutionGroup *getExecutor()
+ ExecutionGroup *getExecutor() const
{
return this->m_executor;
}
@@ -87,7 +92,7 @@ class MemoryProxy {
* \brief get the WriteBufferOperation that is responsible for writing to this MemoryProxy
* \return WriteBufferOperation
*/
- WriteBufferOperation *getWriteBufferOperation()
+ WriteBufferOperation *getWriteBufferOperation() const
{
return this->m_writeBufferOperation;
}
@@ -119,3 +124,5 @@ class MemoryProxy {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryProxy")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MetaData.cc b/source/blender/compositor/intern/COM_MetaData.cc
index ad72b242c8c..88bfa385514 100644
--- a/source/blender/compositor/intern/COM_MetaData.cc
+++ b/source/blender/compositor/intern/COM_MetaData.cc
@@ -24,6 +24,8 @@
#include <string_view>
+namespace blender::compositor {
+
void MetaData::add(const blender::StringRef key, const blender::StringRef value)
{
entries_.add(key, value);
@@ -64,7 +66,7 @@ void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_
void MetaData::addToRenderResult(RenderResult *render_result) const
{
- for (blender::Map<std::string, std::string>::Item entry : entries_.items()) {
+ for (Map<std::string, std::string>::Item entry : entries_.items()) {
BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str());
}
}
@@ -104,3 +106,5 @@ void MetaDataExtractCallbackData::extract_cryptomatte_meta_data(void *_data,
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MetaData.h b/source/blender/compositor/intern/COM_MetaData.h
index fa3de895b4e..a76540dc3af 100644
--- a/source/blender/compositor/intern/COM_MetaData.h
+++ b/source/blender/compositor/intern/COM_MetaData.h
@@ -28,6 +28,8 @@
/* Forward declarations. */
struct RenderResult;
+namespace blender::compositor {
+
/* 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
@@ -41,7 +43,7 @@ constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_NAME("cryptomatte/{hash}/
class MetaData {
private:
- blender::Map<std::string, std::string> entries_;
+ Map<std::string, std::string> entries_;
void addCryptomatteEntry(const blender::StringRef layer_name,
const blender::StringRefNull key,
const blender::StringRef value);
@@ -69,3 +71,5 @@ struct MetaDataExtractCallbackData {
char *propvalue,
int UNUSED(len));
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
new file mode 100644
index 00000000000..c54c2edccb0
--- /dev/null
+++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
@@ -0,0 +1,26 @@
+#include "COM_MultiThreadedOperation.h"
+#include "COM_ExecutionSystem.h"
+
+namespace blender::compositor {
+
+MultiThreadedOperation::MultiThreadedOperation()
+{
+ m_num_passes = 1;
+ flags.is_fullframe_operation = true;
+}
+
+void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output,
+ const rcti &output_area,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system)
+{
+ for (int current_pass = 0; current_pass < m_num_passes; current_pass++) {
+ update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass);
+ exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) {
+ update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass);
+ });
+ update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h
new file mode 100644
index 00000000000..97c5fba4ead
--- /dev/null
+++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h
@@ -0,0 +1,73 @@
+/*
+ * 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 "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+class MultiThreadedOperation : public NodeOperation {
+ protected:
+ /**
+ * Number of execution passes.
+ */
+ int m_num_passes;
+
+ protected:
+ MultiThreadedOperation();
+
+ /**
+ * Called before an update memory buffer pass is executed. Single-threaded calls.
+ */
+ virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_rect),
+ blender::Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system),
+ int UNUSED(current_pass))
+ {
+ }
+
+ /**
+ * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls.
+ */
+ virtual void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &output_rect,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system,
+ int current_pass) = 0;
+
+ /**
+ * Called after an update memory buffer pass is executed. Single-threaded calls.
+ */
+ virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_rect),
+ blender::Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system),
+ int UNUSED(current_pass))
+ {
+ }
+
+ private:
+ void update_memory_buffer(MemoryBuffer *output,
+ const rcti &output_area,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Node.cc b/source/blender/compositor/intern/COM_Node.cc
index 819f1c02b14..6ac48e3646c 100644
--- a/source/blender/compositor/intern/COM_Node.cc
+++ b/source/blender/compositor/intern/COM_Node.cc
@@ -32,6 +32,8 @@
#include "COM_Node.h" /* own include */
+namespace blender::compositor {
+
/**************
**** Node ****
**************/
@@ -74,13 +76,11 @@ Node::Node(bNode *editorNode, bool create_sockets)
Node::~Node()
{
- while (!this->m_outputsockets.empty()) {
- delete (this->m_outputsockets.back());
- this->m_outputsockets.pop_back();
+ while (!this->outputs.is_empty()) {
+ delete (this->outputs.pop_last());
}
- while (!this->m_inputsockets.empty()) {
- delete (this->m_inputsockets.back());
- this->m_inputsockets.pop_back();
+ while (!this->inputs.is_empty()) {
+ delete (this->inputs.pop_last());
}
}
@@ -92,7 +92,7 @@ void Node::addInputSocket(DataType datatype)
void Node::addInputSocket(DataType datatype, bNodeSocket *bSocket)
{
NodeInput *socket = new NodeInput(this, bSocket, datatype);
- this->m_inputsockets.push_back(socket);
+ this->inputs.append(socket);
}
void Node::addOutputSocket(DataType datatype)
@@ -102,19 +102,17 @@ void Node::addOutputSocket(DataType datatype)
void Node::addOutputSocket(DataType datatype, bNodeSocket *bSocket)
{
NodeOutput *socket = new NodeOutput(this, bSocket, datatype);
- this->m_outputsockets.push_back(socket);
+ outputs.append(socket);
}
NodeOutput *Node::getOutputSocket(unsigned int index) const
{
- BLI_assert(index < this->m_outputsockets.size());
- return this->m_outputsockets[index];
+ return outputs[index];
}
NodeInput *Node::getInputSocket(unsigned int index) const
{
- BLI_assert(index < this->m_inputsockets.size());
- return this->m_inputsockets[index];
+ return inputs[index];
}
bNodeSocket *Node::getEditorInputSocket(int editorNodeInputSocketIndex)
@@ -208,3 +206,5 @@ void NodeOutput::getEditorValueVector(float *value)
RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr);
return RNA_float_get_array(&ptr, "default_value", value);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h
index 99f1b58b5ef..28b59397af9 100644
--- a/source/blender/compositor/intern/COM_Node.h
+++ b/source/blender/compositor/intern/COM_Node.h
@@ -18,10 +18,12 @@
#pragma once
+#include "BLI_vector.hh"
+
#include "DNA_node_types.h"
+
#include <algorithm>
#include <string>
-#include <vector>
/* common node includes
* added here so node files don't have to include themselves
@@ -29,7 +31,8 @@
#include "COM_CompositorContext.h"
#include "COM_NodeConverter.h"
-class Node;
+namespace blender::compositor {
+
class NodeOperation;
class NodeConverter;
@@ -37,10 +40,6 @@ class NodeConverter;
* My node documentation.
*/
class Node {
- public:
- typedef std::vector<NodeInput *> Inputs;
- typedef std::vector<NodeOutput *> Outputs;
-
private:
/**
* \brief stores the reference to the SDNA bNode struct
@@ -53,16 +52,6 @@ class Node {
bNode *m_editorNode;
/**
- * \brief the list of actual inputsockets \see NodeInput
- */
- Inputs m_inputsockets;
-
- /**
- * \brief the list of actual outputsockets \see NodeOutput
- */
- Outputs m_outputsockets;
-
- /**
* \brief Is this node part of the active group
*/
bool m_inActiveGroup;
@@ -74,20 +63,14 @@ class Node {
protected:
/**
- * \brief get access to the vector of input sockets
+ * \brief the list of actual input-sockets \see NodeInput
*/
- const Inputs &getInputSockets() const
- {
- return this->m_inputsockets;
- }
+ Vector<NodeInput *> inputs;
/**
- * \brief get access to the vector of input sockets
+ * \brief the list of actual output-sockets \see NodeOutput
*/
- const Outputs &getOutputSockets() const
- {
- return this->m_outputsockets;
- }
+ Vector<NodeOutput *> outputs;
public:
Node(bNode *editorNode, bool create_sockets = true);
@@ -130,19 +113,19 @@ class Node {
}
/**
- * \brief Return the number of input sockets of this node.
+ * \brief get access to the vector of input sockets
*/
- unsigned int getNumberOfInputSockets() const
+ const Vector<NodeInput *> &getInputSockets() const
{
- return this->m_inputsockets.size();
+ return this->inputs;
}
/**
- * \brief Return the number of output sockets of this node.
+ * \brief get access to the vector of input sockets
*/
- unsigned int getNumberOfOutputSockets() const
+ const Vector<NodeOutput *> &getOutputSockets() const
{
- return this->m_outputsockets.size();
+ return this->outputs;
}
/**
@@ -150,17 +133,7 @@ class Node {
* \param index:
* the index of the needed outputsocket
*/
- NodeOutput *getOutputSocket(const unsigned int index) const;
-
- /**
- * get the reference to the first outputsocket
- * \param index:
- * the index of the needed outputsocket
- */
- inline NodeOutput *getOutputSocket() const
- {
- return getOutputSocket(0);
- }
+ NodeOutput *getOutputSocket(const unsigned int index = 0) const;
/**
* get the reference to a certain inputsocket
@@ -169,14 +142,6 @@ class Node {
*/
NodeInput *getInputSocket(const unsigned int index) const;
- /** Check if this is an input node
- * An input node is a node that only has output sockets and no input sockets
- */
- bool isInputNode() const
- {
- return m_inputsockets.empty();
- }
-
/**
* \brief Is this node in the active group (the group that is being edited)
* \param isInActiveGroup:
@@ -219,7 +184,7 @@ class Node {
protected:
/**
- * \brief add an NodeInput to the collection of inputsockets
+ * \brief add an NodeInput to the collection of input-sockets
* \note may only be called in an constructor
* \param socket: the NodeInput to add
*/
@@ -227,7 +192,7 @@ class Node {
void addInputSocket(DataType datatype, bNodeSocket *socket);
/**
- * \brief add an NodeOutput to the collection of outputsockets
+ * \brief add an NodeOutput to the collection of output-sockets
* \note may only be called in an constructor
* \param socket: the NodeOutput to add
*/
@@ -317,3 +282,5 @@ class NodeOutput {
void getEditorValueColor(float *value);
void getEditorValueVector(float *value);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeConverter.cc b/source/blender/compositor/intern/COM_NodeConverter.cc
index 2db31bd4133..49a2e7988c4 100644
--- a/source/blender/compositor/intern/COM_NodeConverter.cc
+++ b/source/blender/compositor/intern/COM_NodeConverter.cc
@@ -29,6 +29,8 @@
#include "COM_NodeConverter.h" /* own include */
+namespace blender::compositor {
+
NodeConverter::NodeConverter(NodeOperationBuilder *builder) : m_builder(builder)
{
}
@@ -160,3 +162,5 @@ ViewerOperation *NodeConverter::active_viewer() const
{
return m_builder->active_viewer();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeConverter.h b/source/blender/compositor/intern/COM_NodeConverter.h
index e9b05184857..b3f03485249 100644
--- a/source/blender/compositor/intern/COM_NodeConverter.h
+++ b/source/blender/compositor/intern/COM_NodeConverter.h
@@ -22,6 +22,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
class NodeInput;
class NodeOutput;
@@ -120,3 +122,5 @@ class NodeConverter {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompiler")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc
index d8220099f1f..fbe56dd4b5a 100644
--- a/source/blender/compositor/intern/COM_NodeGraph.cc
+++ b/source/blender/compositor/intern/COM_NodeGraph.cc
@@ -33,19 +33,16 @@
#include "COM_NodeGraph.h" /* own include */
+namespace blender::compositor {
+
/*******************
**** NodeGraph ****
*******************/
-NodeGraph::NodeGraph()
-{
-}
-
NodeGraph::~NodeGraph()
{
- for (int index = 0; index < this->m_nodes.size(); index++) {
- Node *node = this->m_nodes[index];
- delete node;
+ while (m_nodes.size()) {
+ delete m_nodes.pop_last();
}
}
@@ -83,7 +80,7 @@ void NodeGraph::add_node(Node *node,
node->setInstanceKey(key);
node->setIsInActiveGroup(is_active_group);
- m_nodes.push_back(node);
+ m_nodes.append(node);
DebugInfo::node_added(node);
}
@@ -153,27 +150,11 @@ void NodeGraph::add_bNode(const CompositorContext &context,
}
}
-NodeGraph::NodeInputs NodeGraph::find_inputs(const NodeRange &node_range, bNodeSocket *b_socket)
-{
- NodeInputs result;
- for (NodeGraph::NodeIterator it = node_range.first; it != node_range.second; ++it) {
- Node *node = *it;
- for (int index = 0; index < node->getNumberOfInputSockets(); index++) {
- NodeInput *input = node->getInputSocket(index);
- if (input->getbNodeSocket() == b_socket) {
- result.push_back(input);
- }
- }
- }
- return result;
-}
-
NodeOutput *NodeGraph::find_output(const NodeRange &node_range, bNodeSocket *b_socket)
{
- for (NodeGraph::NodeIterator it = node_range.first; it != node_range.second; ++it) {
+ for (Vector<Node *>::iterator it = node_range.first; it != node_range.second; ++it) {
Node *node = *it;
- for (int index = 0; index < node->getNumberOfOutputSockets(); index++) {
- NodeOutput *output = node->getOutputSocket(index);
+ for (NodeOutput *output : node->getOutputSockets()) {
if (output->getbNodeSocket() == b_socket) {
return output;
}
@@ -202,12 +183,13 @@ void NodeGraph::add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink
return;
}
- NodeInputs inputs = find_inputs(node_range, b_nodelink->tosock);
- for (NodeInput *input : inputs) {
- if (input->isLinked()) {
- continue;
+ for (Vector<Node *>::iterator it = node_range.first; it != node_range.second; ++it) {
+ Node *node = *it;
+ for (NodeInput *input : node->getInputSockets()) {
+ if (input->getbNodeSocket() == b_nodelink->tosock && !input->isLinked()) {
+ add_link(output, input);
+ }
}
- add_link(output, input);
}
}
@@ -331,3 +313,5 @@ void NodeGraph::add_proxies_reroute(bNodeTree *b_ntree,
b_node, (bNodeSocket *)b_node->inputs.first, (bNodeSocket *)b_node->outputs.first, false);
add_node(proxy, b_ntree, key, is_active_group);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h
index 990e3a30831..7fa01593e1e 100644
--- a/source/blender/compositor/intern/COM_NodeGraph.h
+++ b/source/blender/compositor/intern/COM_NodeGraph.h
@@ -22,7 +22,6 @@
#include <map>
#include <set>
-#include <vector>
#include "DNA_node_types.h"
@@ -30,6 +29,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
class CompositorContext;
class Node;
class NodeInput;
@@ -50,22 +51,18 @@ class NodeGraph {
}
};
- typedef std::vector<Node *> Nodes;
- typedef Nodes::iterator NodeIterator;
-
private:
- Nodes m_nodes;
- blender::Vector<Link> m_links;
+ Vector<Node *> m_nodes;
+ Vector<Link> m_links;
public:
- NodeGraph();
~NodeGraph();
- const Nodes &nodes() const
+ const Vector<Node *> &nodes() const
{
return m_nodes;
}
- const blender::Vector<Link> &links() const
+ const Vector<Link> &links() const
{
return m_links;
}
@@ -73,8 +70,7 @@ class NodeGraph {
void from_bNodeTree(const CompositorContext &context, bNodeTree *tree);
protected:
- typedef std::pair<NodeIterator, NodeIterator> NodeRange;
- typedef std::vector<NodeInput *> NodeInputs;
+ typedef std::pair<Vector<Node *>::iterator, Vector<Node *>::iterator> NodeRange;
static bNodeSocket *find_b_node_input(bNode *b_node, const char *identifier);
static bNodeSocket *find_b_node_output(bNode *b_node, const char *identifier);
@@ -93,7 +89,6 @@ class NodeGraph {
bNodeInstanceKey key,
bool is_active_group);
- NodeInputs find_inputs(const NodeRange &node_range, bNodeSocket *b_socket);
NodeOutput *find_output(const NodeRange &node_range, bNodeSocket *b_socket);
void add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink);
@@ -124,3 +119,5 @@ class NodeGraph {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeGraph")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc
index 0cc642479ac..83de8a751c4 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -17,13 +17,18 @@
*/
#include <cstdio>
+#include <memory>
#include <typeinfo>
+#include "COM_BufferOperation.h"
#include "COM_ExecutionSystem.h"
+#include "COM_ReadBufferOperation.h"
#include "COM_defines.h"
#include "COM_NodeOperation.h" /* own include */
+namespace blender::compositor {
+
/*******************
**** NodeOperation ****
*******************/
@@ -31,76 +36,66 @@
NodeOperation::NodeOperation()
{
this->m_resolutionInputSocketIndex = 0;
- this->m_complex = false;
this->m_width = 0;
this->m_height = 0;
- this->m_isResolutionSet = false;
- this->m_openCL = false;
this->m_btree = nullptr;
}
-NodeOperation::~NodeOperation()
+NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index)
{
- while (!this->m_outputs.empty()) {
- delete (this->m_outputs.back());
- this->m_outputs.pop_back();
- }
- while (!this->m_inputs.empty()) {
- delete (this->m_inputs.back());
- this->m_inputs.pop_back();
- }
+ return &m_outputs[index];
}
-NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index) const
+NodeOperationInput *NodeOperation::getInputSocket(unsigned int index)
{
- BLI_assert(index < m_outputs.size());
- return m_outputs[index];
+ return &m_inputs[index];
}
-NodeOperationInput *NodeOperation::getInputSocket(unsigned int index) const
+void NodeOperation::addInputSocket(DataType datatype, ResizeMode resize_mode)
{
- BLI_assert(index < m_inputs.size());
- return m_inputs[index];
-}
-
-void NodeOperation::addInputSocket(DataType datatype, InputResizeMode resize_mode)
-{
- NodeOperationInput *socket = new NodeOperationInput(this, datatype, resize_mode);
- m_inputs.push_back(socket);
+ m_inputs.append(NodeOperationInput(this, datatype, resize_mode));
}
void NodeOperation::addOutputSocket(DataType datatype)
{
- NodeOperationOutput *socket = new NodeOperationOutput(this, datatype);
- m_outputs.push_back(socket);
+ m_outputs.append(NodeOperationOutput(this, datatype));
}
void NodeOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
- unsigned int temp[2];
- unsigned int temp2[2];
-
- for (unsigned int index = 0; index < m_inputs.size(); index++) {
- NodeOperationInput *input = m_inputs[index];
- if (input->isConnected()) {
- if (index == this->m_resolutionInputSocketIndex) {
- input->determineResolution(resolution, preferredResolution);
- temp2[0] = resolution[0];
- temp2[1] = resolution[1];
+ unsigned int used_resolution_index = 0;
+ if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) {
+ for (NodeOperationInput &input : m_inputs) {
+ unsigned int any_resolution[2] = {0, 0};
+ input.determineResolution(any_resolution, preferredResolution);
+ if (any_resolution[0] * any_resolution[1] > 0) {
+ resolution[0] = any_resolution[0];
+ resolution[1] = any_resolution[1];
break;
}
+ used_resolution_index += 1;
}
}
+ else if (m_resolutionInputSocketIndex < m_inputs.size()) {
+ NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex];
+ input.determineResolution(resolution, preferredResolution);
+ used_resolution_index = m_resolutionInputSocketIndex;
+ }
+ unsigned int temp2[2] = {resolution[0], resolution[1]};
+
+ unsigned int temp[2];
for (unsigned int index = 0; index < m_inputs.size(); index++) {
- NodeOperationInput *input = m_inputs[index];
- if (input->isConnected()) {
- if (index != this->m_resolutionInputSocketIndex) {
- input->determineResolution(temp, temp2);
- }
+ if (index == used_resolution_index) {
+ continue;
+ }
+ NodeOperationInput &input = m_inputs[index];
+ if (input.isConnected()) {
+ input.determineResolution(temp, temp2);
}
}
}
+
void NodeOperation::setResolutionInputSocketIndex(unsigned int index)
{
this->m_resolutionInputSocketIndex = index;
@@ -149,20 +144,11 @@ NodeOperation *NodeOperation::getInputOperation(unsigned int inputSocketIndex)
return nullptr;
}
-void NodeOperation::getConnectedInputSockets(Inputs *sockets)
-{
- for (NodeOperationInput *input : m_inputs) {
- if (input->isConnected()) {
- sockets->push_back(input);
- }
- }
-}
-
bool NodeOperation::determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output)
{
- if (isInputOperation()) {
+ if (m_inputs.size() == 0) {
BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax);
return false;
}
@@ -191,13 +177,182 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input,
return !first;
}
+/* -------------------------------------------------------------------- */
+/** \name Full Frame Methods
+ * \{ */
+
+/**
+ * \brief Get input operation area being read by this operation on rendering given output area.
+ *
+ * Implementation don't need to ensure r_input_area is within input operation bounds. The
+ * caller must clamp it.
+ * TODO: See if it's possible to use parameter overloading (input_id for example).
+ *
+ * \param input_op_idx: Input operation index for which we want to calculate the area being read.
+ * \param output_area: Area being rendered by this operation.
+ * \param r_input_area: Returned input operation area that needs to be read in order to render
+ * given output area.
+ */
+void NodeOperation::get_area_of_interest(const int input_op_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (get_flags().is_fullframe_operation) {
+ r_input_area = output_area;
+ }
+ else {
+ /* Non full-frame operations never implement this method. To ensure correctness assume
+ * whole area is used. */
+ NodeOperation *input_op = getInputOperation(input_op_idx);
+ BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight());
+ }
+}
+
+void NodeOperation::get_area_of_interest(NodeOperation *input_op,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ for (int i = 0; i < getNumberOfInputSockets(); i++) {
+ if (input_op == getInputOperation(i)) {
+ get_area_of_interest(i, output_area, r_input_area);
+ return;
+ }
+ }
+ BLI_assert(!"input_op is not an input operation.");
+}
+
+/**
+ * Executes operation image manipulation algorithm rendering given areas.
+ * \param output_buf: Buffer to write result to.
+ * \param areas: Areas within this operation bounds to render.
+ * \param inputs_bufs: Inputs operations buffers.
+ * \param exec_system: Execution system.
+ */
+void NodeOperation::render(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ if (get_flags().is_fullframe_operation) {
+ render_full_frame(output_buf, areas, inputs_bufs, exec_system);
+ }
+ else {
+ render_full_frame_fallback(output_buf, areas, inputs_bufs, exec_system);
+ }
+}
+
+/**
+ * Renders given areas using operations full frame implementation.
+ */
+void NodeOperation::render_full_frame(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ initExecution();
+ for (const rcti &area : areas) {
+ update_memory_buffer(output_buf, area, inputs_bufs, exec_system);
+ }
+ deinitExecution();
+}
+
+/**
+ * Renders given areas using operations tiled implementation.
+ */
+void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ Vector<NodeOperationOutput *> orig_input_links = replace_inputs_with_buffers(inputs_bufs);
+
+ initExecution();
+ const bool is_output_operation = getNumberOfOutputSockets() == 0;
+ if (!is_output_operation && output_buf->is_a_single_elem()) {
+ float *output_elem = output_buf->get_elem(0, 0);
+ readSampled(output_elem, 0, 0, PixelSampler::Nearest);
+ }
+ else {
+ for (const rcti &rect : areas) {
+ exec_system.execute_work(rect, [=](const rcti &split_rect) {
+ rcti tile_rect = split_rect;
+ if (is_output_operation) {
+ executeRegion(&tile_rect, 0);
+ }
+ else {
+ render_tile(output_buf, &tile_rect);
+ }
+ });
+ }
+ }
+ deinitExecution();
+
+ remove_buffers_and_restore_original_inputs(orig_input_links);
+}
+
+void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect)
+{
+ const bool is_complex = get_flags().complex;
+ void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr;
+ const int elem_stride = output_buf->elem_stride;
+ for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) {
+ float *output_elem = output_buf->get_elem(tile_rect->xmin, y);
+ if (is_complex) {
+ for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) {
+ read(output_elem, x, y, tile_data);
+ output_elem += elem_stride;
+ }
+ }
+ else {
+ for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) {
+ readSampled(output_elem, x, y, PixelSampler::Nearest);
+ output_elem += elem_stride;
+ }
+ }
+ }
+ if (tile_data) {
+ deinitializeTileData(tile_rect, tile_data);
+ }
+}
+
+/**
+ * \return Replaced inputs links.
+ */
+Vector<NodeOperationOutput *> NodeOperation::replace_inputs_with_buffers(
+ Span<MemoryBuffer *> inputs_bufs)
+{
+ BLI_assert(inputs_bufs.size() == getNumberOfInputSockets());
+ Vector<NodeOperationOutput *> orig_links(inputs_bufs.size());
+ for (int i = 0; i < inputs_bufs.size(); i++) {
+ NodeOperationInput *input_socket = getInputSocket(i);
+ BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType());
+ orig_links[i] = input_socket->getLink();
+ input_socket->setLink(buffer_op->getOutputSocket());
+ }
+ return orig_links;
+}
+
+void NodeOperation::remove_buffers_and_restore_original_inputs(
+ Span<NodeOperationOutput *> original_inputs_links)
+{
+ BLI_assert(original_inputs_links.size() == getNumberOfInputSockets());
+ for (int i = 0; i < original_inputs_links.size(); i++) {
+ NodeOperation *buffer_op = get_input_operation(i);
+ BLI_assert(buffer_op != nullptr);
+ BLI_assert(typeid(*buffer_op) == typeid(BufferOperation));
+ NodeOperationInput *input_socket = getInputSocket(i);
+ input_socket->setLink(original_inputs_links[i]);
+ delete buffer_op;
+ }
+}
+
+/** \} */
+
/*****************
**** OpInput ****
*****************/
-NodeOperationInput::NodeOperationInput(NodeOperation *op,
- DataType datatype,
- InputResizeMode resizeMode)
+NodeOperationInput::NodeOperationInput(NodeOperation *op, DataType datatype, ResizeMode resizeMode)
: m_operation(op), m_datatype(datatype), m_resizeMode(resizeMode), m_link(nullptr)
{
}
@@ -232,12 +387,88 @@ void NodeOperationOutput::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
NodeOperation &operation = getOperation();
- if (operation.isResolutionSet()) {
+ if (operation.get_flags().is_resolution_set) {
resolution[0] = operation.getWidth();
resolution[1] = operation.getHeight();
}
else {
operation.determineResolution(resolution, preferredResolution);
- operation.setResolution(resolution);
+ if (resolution[0] > 0 && resolution[1] > 0) {
+ operation.setResolution(resolution);
+ }
+ }
+}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operation_flags)
+{
+ if (node_operation_flags.complex) {
+ os << "complex,";
+ }
+ if (node_operation_flags.open_cl) {
+ os << "open_cl,";
+ }
+ if (node_operation_flags.single_threaded) {
+ os << "single_threaded,";
+ }
+ if (node_operation_flags.use_render_border) {
+ os << "render_border,";
+ }
+ if (node_operation_flags.use_viewer_border) {
+ os << "view_border,";
+ }
+ if (node_operation_flags.is_resolution_set) {
+ os << "resolution_set,";
+ }
+ if (node_operation_flags.is_set_operation) {
+ os << "set_operation,";
+ }
+ if (node_operation_flags.is_write_buffer_operation) {
+ os << "write_buffer,";
+ }
+ if (node_operation_flags.is_read_buffer_operation) {
+ os << "read_buffer,";
+ }
+ if (node_operation_flags.is_proxy_operation) {
+ os << "proxy,";
+ }
+ if (node_operation_flags.is_viewer_operation) {
+ os << "viewer,";
}
+ if (node_operation_flags.is_preview_operation) {
+ os << "preview,";
+ }
+ if (!node_operation_flags.use_datatype_conversion) {
+ os << "no_conversion,";
+ }
+ if (node_operation_flags.is_fullframe_operation) {
+ os << "full_frame,";
+ }
+
+ return os;
}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation)
+{
+ NodeOperationFlags flags = node_operation.get_flags();
+ os << "NodeOperation(";
+ os << "id=" << node_operation.get_id();
+ if (!node_operation.get_name().empty()) {
+ os << ",name=" << node_operation.get_name();
+ }
+ os << ",flags={" << flags << "}";
+ if (flags.is_read_buffer_operation) {
+ const ReadBufferOperation *read_operation = (const ReadBufferOperation *)&node_operation;
+ const MemoryProxy *proxy = read_operation->getMemoryProxy();
+ if (proxy) {
+ const WriteBufferOperation *write_operation = proxy->getWriteBufferOperation();
+ if (write_operation) {
+ os << ",write=" << (NodeOperation &)*write_operation;
+ }
+ }
+ }
+ os << ")";
+
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index 7857837a95d..5c4d6dd19ba 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -26,77 +26,258 @@
#include "BLI_math_vector.h"
#include "BLI_threads.h"
+#include "COM_Enums.h"
#include "COM_MemoryBuffer.h"
#include "COM_MemoryProxy.h"
+#include "COM_MetaData.h"
#include "COM_Node.h"
-#include "COM_SocketReader.h"
#include "clew.h"
+namespace blender::compositor {
+
class OpenCLDevice;
class ReadBufferOperation;
class WriteBufferOperation;
+class ExecutionSystem;
+
+class NodeOperation;
+typedef NodeOperation SocketReader;
-class NodeOperationInput;
-class NodeOperationOutput;
+/**
+ * RESOLUTION_INPUT_ANY is a wildcard when any resolution of an input can be used.
+ * This solves the issue that the FileInputNode in a group node cannot find the
+ * correct resolution.
+ */
+static constexpr unsigned int RESOLUTION_INPUT_ANY = 999999;
/**
* \brief Resize modes of inputsockets
* How are the input and working resolutions matched
* \ingroup Model
*/
-typedef enum InputResizeMode {
+enum class ResizeMode {
/** \brief Center the input image to the center of the working area of the node, no resizing
* occurs */
- COM_SC_CENTER = NS_CR_CENTER,
+ Center = NS_CR_CENTER,
/** \brief The bottom left of the input image is the bottom left of the working area of the node,
* no resizing occurs */
- COM_SC_NO_RESIZE = NS_CR_NONE,
+ None = NS_CR_NONE,
/** \brief Fit the width of the input image to the width of the working area of the node */
- COM_SC_FIT_WIDTH = NS_CR_FIT_WIDTH,
+ FitWidth = NS_CR_FIT_WIDTH,
/** \brief Fit the height of the input image to the height of the working area of the node */
- COM_SC_FIT_HEIGHT = NS_CR_FIT_HEIGHT,
+ FitHeight = NS_CR_FIT_HEIGHT,
/** \brief Fit the width or the height of the input image to the width or height of the working
* area of the node, image will be larger than the working area */
- COM_SC_FIT = NS_CR_FIT,
+ FitAny = NS_CR_FIT,
/** \brief Fit the width and the height of the input image to the width and height of the working
* area of the node, image will be equally larger than the working area */
- COM_SC_STRETCH = NS_CR_STRETCH,
-} InputResizeMode;
+ Stretch = NS_CR_STRETCH,
+};
+
+enum class PixelSampler {
+ Nearest = 0,
+ Bilinear = 1,
+ Bicubic = 2,
+};
+
+class NodeOperationInput {
+ private:
+ NodeOperation *m_operation;
+
+ /** Datatype of this socket. Is used for automatically data transformation.
+ * \section data-conversion
+ */
+ DataType m_datatype;
+
+ /** Resize mode of this socket */
+ ResizeMode m_resizeMode;
+
+ /** Connected output */
+ NodeOperationOutput *m_link;
-/**
- * \brief NodeOperation contains calculation logic
- *
- * Subclasses needs to implement the execution method (defined in SocketReader) to implement logic.
- * \ingroup Model
- */
-class NodeOperation : public SocketReader {
public:
- typedef std::vector<NodeOperationInput *> Inputs;
- typedef std::vector<NodeOperationOutput *> Outputs;
+ NodeOperationInput(NodeOperation *op,
+ DataType datatype,
+ ResizeMode resizeMode = ResizeMode::Center);
+ NodeOperation &getOperation() const
+ {
+ return *m_operation;
+ }
+ DataType getDataType() const
+ {
+ return m_datatype;
+ }
+
+ void setLink(NodeOperationOutput *link)
+ {
+ m_link = link;
+ }
+ NodeOperationOutput *getLink() const
+ {
+ return m_link;
+ }
+ bool isConnected() const
+ {
+ return m_link;
+ }
+
+ void setResizeMode(ResizeMode resizeMode)
+ {
+ this->m_resizeMode = resizeMode;
+ }
+ ResizeMode getResizeMode() const
+ {
+ return this->m_resizeMode;
+ }
+
+ SocketReader *getReader();
+
+ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
+#endif
+};
+
+class NodeOperationOutput {
private:
- Inputs m_inputs;
- Outputs m_outputs;
+ NodeOperation *m_operation;
+
+ /** Datatype of this socket. Is used for automatically data transformation.
+ * \section data-conversion
+ */
+ DataType m_datatype;
+
+ public:
+ NodeOperationOutput(NodeOperation *op, DataType datatype);
+
+ NodeOperation &getOperation() const
+ {
+ return *m_operation;
+ }
+ DataType getDataType() const
+ {
+ return m_datatype;
+ }
/**
- * \brief the index of the input socket that will be used to determine the resolution
+ * \brief determine the resolution of this data going through this socket
+ * \param resolution: the result of this operation
+ * \param preferredResolution: the preferable resolution as no resolution could be determined
*/
- unsigned int m_resolutionInputSocketIndex;
+ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
+#endif
+};
+
+struct NodeOperationFlags {
/**
- * \brief is this operation a complex one.
+ * Is this an complex operation.
+ *
+ * The input and output buffers of Complex operations are stored in buffers. It allows
+ * sequential and read/write.
*
* Complex operations are typically doing many reads to calculate the output of a single pixel.
* Mostly Filter types (Blurs, Convolution, Defocus etc) need this to be set to true.
*/
- bool m_complex;
+ bool complex : 1;
+
+ /**
+ * Does this operation support OpenCL.
+ */
+ bool open_cl : 1;
+
+ /**
+ * TODO: Remove this flag and #SingleThreadedOperation if tiled implementation is removed.
+ * Full-frame implementation doesn't need it.
+ */
+ bool single_threaded : 1;
/**
- * \brief can this operation be scheduled on an OpenCL device.
- * \note Only applicable if complex is True
+ * Does the operation needs a viewer border.
+ * Basically, setting border need to happen for only operations
+ * which operates in render resolution buffers (like compositor
+ * output nodes).
+ *
+ * In this cases adding border will lead to mapping coordinates
+ * from output buffer space to input buffer spaces when executing
+ * operation.
+ *
+ * But nodes like viewer and file output just shall display or
+ * safe the same exact buffer which goes to their input, no need
+ * in any kind of coordinates mapping.
+ */
+ bool use_render_border : 1;
+ bool use_viewer_border : 1;
+
+ /**
+ * Is the resolution of the operation set.
*/
- bool m_openCL;
+ bool is_resolution_set : 1;
+
+ /**
+ * Is this a set operation (value, color, vector).
+ */
+ bool is_set_operation : 1;
+ bool is_write_buffer_operation : 1;
+ bool is_read_buffer_operation : 1;
+ bool is_proxy_operation : 1;
+ bool is_viewer_operation : 1;
+ bool is_preview_operation : 1;
+
+ /**
+ * When set additional data conversion operations are added to
+ * convert the data. SocketProxyOperation don't always need to do data conversions.
+ *
+ * By default data conversions are enabled.
+ */
+ bool use_datatype_conversion : 1;
+
+ /**
+ * Has this operation fullframe implementation.
+ */
+ bool is_fullframe_operation : 1;
+
+ NodeOperationFlags()
+ {
+ complex = false;
+ single_threaded = false;
+ open_cl = false;
+ use_render_border = false;
+ use_viewer_border = false;
+ is_resolution_set = false;
+ is_set_operation = false;
+ is_read_buffer_operation = false;
+ is_write_buffer_operation = false;
+ is_proxy_operation = false;
+ is_viewer_operation = false;
+ is_preview_operation = false;
+ use_datatype_conversion = true;
+ is_fullframe_operation = false;
+ }
+};
+
+/**
+ * \brief NodeOperation contains calculation logic
+ *
+ * Subclasses needs to implement the execution method (defined in SocketReader) to implement logic.
+ * \ingroup Model
+ */
+class NodeOperation {
+ private:
+ int m_id;
+ std::string m_name;
+ Vector<NodeOperationInput> m_inputs;
+ Vector<NodeOperationOutput> m_outputs;
+
+ /**
+ * \brief the index of the input socket that will be used to determine the resolution
+ */
+ unsigned int m_resolutionInputSocketIndex;
/**
* \brief mutex reference for very special node initializations
@@ -114,13 +295,61 @@ class NodeOperation : public SocketReader {
*/
const bNodeTree *m_btree;
+ protected:
/**
- * \brief set to truth when resolution for this operation is set
+ * Compositor execution model.
*/
- bool m_isResolutionSet;
+ eExecutionModel execution_model_;
+
+ /**
+ * Width of the output of this operation.
+ */
+ unsigned int m_width;
+
+ /**
+ * Height of the output of this operation.
+ */
+ unsigned int m_height;
+
+ /**
+ * Flags how to evaluate this operation.
+ */
+ NodeOperationFlags flags;
public:
- virtual ~NodeOperation();
+ virtual ~NodeOperation()
+ {
+ }
+
+ void set_execution_model(const eExecutionModel model)
+ {
+ execution_model_ = model;
+ }
+
+ void set_name(const std::string name)
+ {
+ m_name = name;
+ }
+
+ const std::string get_name() const
+ {
+ return m_name;
+ }
+
+ void set_id(const int id)
+ {
+ m_id = id;
+ }
+
+ const int get_id() const
+ {
+ return m_id;
+ }
+
+ const NodeOperationFlags get_flags() const
+ {
+ return flags;
+ }
unsigned int getNumberOfInputSockets() const
{
@@ -130,19 +359,14 @@ class NodeOperation : public SocketReader {
{
return m_outputs.size();
}
- NodeOperationOutput *getOutputSocket(unsigned int index) const;
- NodeOperationOutput *getOutputSocket() const
- {
- return getOutputSocket(0);
- }
- NodeOperationInput *getInputSocket(unsigned int index) const;
+ NodeOperationOutput *getOutputSocket(unsigned int index = 0);
+ NodeOperationInput *getInputSocket(unsigned int index);
- /** Check if this is an input operation
- * An input operation is an operation that only has output sockets and no input sockets
- */
- bool isInputOperation() const
+ NodeOperation *get_input_operation(int index)
{
- return m_inputs.empty();
+ /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing
+ * this method. */
+ return getInputOperation(index);
}
/**
@@ -155,11 +379,11 @@ class NodeOperation : public SocketReader {
unsigned int preferredResolution[2]);
/**
- * \brief isOutputOperation determines whether this operation is an output of the ExecutionSystem
- * during rendering or editing.
+ * \brief isOutputOperation determines whether this operation is an output of the
+ * ExecutionSystem during rendering or editing.
*
- * Default behavior if not overridden, this operation will not be evaluated as being an output of
- * the ExecutionSystem.
+ * Default behavior if not overridden, this operation will not be evaluated as being an output
+ * of the ExecutionSystem.
*
* \see ExecutionSystem
* \ingroup check
@@ -174,11 +398,6 @@ class NodeOperation : public SocketReader {
return false;
}
- virtual int isSingleThreaded()
- {
- return false;
- }
-
void setbNodeTree(const bNodeTree *tree)
{
this->m_btree = tree;
@@ -241,62 +460,19 @@ class NodeOperation : public SocketReader {
}
virtual void deinitExecution();
- bool isResolutionSet()
- {
- return this->m_isResolutionSet;
- }
-
/**
* \brief set the resolution
* \param resolution: the resolution to set
*/
void setResolution(unsigned int resolution[2])
{
- if (!isResolutionSet()) {
+ if (!this->flags.is_resolution_set) {
this->m_width = resolution[0];
this->m_height = resolution[1];
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
}
- void getConnectedInputSockets(Inputs *sockets);
-
- /**
- * \brief is this operation complex
- *
- * Complex operations are typically doing many reads to calculate the output of a single pixel.
- * Mostly Filter types (Blurs, Convolution, Defocus etc) need this to be set to true.
- */
- bool isComplex() const
- {
- return this->m_complex;
- }
-
- virtual bool isSetOperation() const
- {
- return false;
- }
-
- /**
- * \brief is this operation of type ReadBufferOperation
- * \return [true:false]
- * \see ReadBufferOperation
- */
- virtual bool isReadBufferOperation() const
- {
- return false;
- }
-
- /**
- * \brief is this operation of type WriteBufferOperation
- * \return [true:false]
- * \see WriteBufferOperation
- */
- virtual bool isWriteBufferOperation() const
- {
- return false;
- }
-
/**
* \brief is this operation the active viewer output
* user can select an ViewerNode to be active
@@ -314,80 +490,123 @@ class NodeOperation : public SocketReader {
rcti *output);
/**
- * \brief set the index of the input socket that will determine the resolution of this operation
- * \param index: the index to set
+ * \brief set the index of the input socket that will determine the resolution of this
+ * operation \param index: the index to set
*/
void setResolutionInputSocketIndex(unsigned int index);
/**
* \brief get the render priority of this node.
* \note only applicable for output operations like ViewerOperation
- * \return CompositorPriority
+ * \return eCompositorPriority
*/
- virtual CompositorPriority getRenderPriority() const
+ virtual eCompositorPriority getRenderPriority() const
{
- return CompositorPriority::Low;
+ return eCompositorPriority::Low;
}
- /**
- * \brief can this NodeOperation be scheduled on an OpenCLDevice
- * \see WorkScheduler.schedule
- * \see ExecutionGroup.addOperation
- */
- bool isOpenCL() const
+ inline bool isBraked() const
{
- return this->m_openCL;
+ return this->m_btree->test_break(this->m_btree->tbh);
}
- virtual bool isViewerOperation() const
+ inline void updateDraw()
{
- return false;
+ if (this->m_btree->update_draw) {
+ this->m_btree->update_draw(this->m_btree->udh);
+ }
}
- virtual bool isPreviewOperation() const
+
+ unsigned int getWidth() const
{
- return false;
+ return m_width;
}
- virtual bool isFileOutputOperation() const
+
+ unsigned int getHeight() const
{
- return false;
+ return m_height;
}
- virtual bool isProxyOperation() const
+
+ inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
{
- return false;
+ executePixelSampled(result, x, y, sampler);
}
- virtual bool useDatatypeConversion() const
+ inline void readFiltered(float result[4], float x, float y, float dx[2], float dy[2])
{
- return true;
+ executePixelFiltered(result, x, y, dx, dy);
}
- inline bool isBraked() const
+ inline void read(float result[4], int x, int y, void *chunkData)
{
- return this->m_btree->test_break(this->m_btree->tbh);
+ executePixel(result, x, y, chunkData);
}
- inline void updateDraw()
+ virtual void *initializeTileData(rcti * /*rect*/)
+ {
+ return 0;
+ }
+
+ virtual void deinitializeTileData(rcti * /*rect*/, void * /*data*/)
{
- if (this->m_btree->update_draw) {
- this->m_btree->update_draw(this->m_btree->udh);
- }
}
+ virtual MemoryBuffer *getInputMemoryBuffer(MemoryBuffer ** /*memoryBuffers*/)
+ {
+ return 0;
+ }
+
+ /**
+ * Return the meta data associated with this branch.
+ *
+ * The return parameter holds an instance or is an nullptr. */
+ virtual std::unique_ptr<MetaData> getMetaData()
+ {
+ return std::unique_ptr<MetaData>();
+ }
+
+ /* -------------------------------------------------------------------- */
+ /** \name Full Frame Methods
+ * \{ */
+
+ void render(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system);
+
+ /**
+ * Executes operation updating output memory buffer. Single-threaded calls.
+ */
+ virtual void update_memory_buffer(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_area),
+ Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system))
+ {
+ }
+
+ /**
+ * Get input operation area being read by this operation on rendering given output area.
+ */
+ virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area);
+ void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area);
+
+ /** \} */
+
protected:
NodeOperation();
- void addInputSocket(DataType datatype, InputResizeMode resize_mode = COM_SC_CENTER);
+ void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center);
void addOutputSocket(DataType datatype);
void setWidth(unsigned int width)
{
this->m_width = width;
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
void setHeight(unsigned int height)
{
this->m_height = height;
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
SocketReader *getInputSocketReader(unsigned int inputSocketindex);
NodeOperation *getInputOperation(unsigned int inputSocketindex);
@@ -405,114 +624,83 @@ class NodeOperation : public SocketReader {
*/
void setComplex(bool complex)
{
- this->m_complex = complex;
+ this->flags.complex = complex;
}
/**
- * \brief set if this NodeOperation can be scheduled on a OpenCLDevice
+ * \brief calculate a single pixel
+ * \note this method is called for non-complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
*/
- void setOpenCL(bool openCL)
+ virtual void executePixelSampled(float /*output*/[4],
+ float /*x*/,
+ float /*y*/,
+ PixelSampler /*sampler*/)
{
- this->m_openCL = openCL;
}
- /* allow the DebugInfo class to look at internals */
- friend class DebugInfo;
-
-#ifdef WITH_CXX_GUARDEDALLOC
- MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
-#endif
-};
-
-class NodeOperationInput {
- private:
- NodeOperation *m_operation;
-
- /** Datatype of this socket. Is used for automatically data transformation.
- * \section data-conversion
+ /**
+ * \brief calculate a single pixel
+ * \note this method is called for complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
+ * \param chunkData: chunk specific data a during execution time.
*/
- DataType m_datatype;
-
- /** Resize mode of this socket */
- InputResizeMode m_resizeMode;
-
- /** Connected output */
- NodeOperationOutput *m_link;
-
- public:
- NodeOperationInput(NodeOperation *op,
- DataType datatype,
- InputResizeMode resizeMode = COM_SC_CENTER);
-
- NodeOperation &getOperation() const
- {
- return *m_operation;
- }
- DataType getDataType() const
- {
- return m_datatype;
- }
-
- void setLink(NodeOperationOutput *link)
+ virtual void executePixel(float output[4], int x, int y, void * /*chunkData*/)
{
- m_link = link;
- }
- NodeOperationOutput *getLink() const
- {
- return m_link;
- }
- bool isConnected() const
- {
- return m_link;
+ executePixelSampled(output, x, y, PixelSampler::Nearest);
}
- void setResizeMode(InputResizeMode resizeMode)
- {
- this->m_resizeMode = resizeMode;
- }
- InputResizeMode getResizeMode() const
+ /**
+ * \brief calculate a single pixel using an EWA filter
+ * \note this method is called for complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param dx:
+ * \param dy:
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
+ */
+ virtual void executePixelFiltered(
+ float /*output*/[4], float /*x*/, float /*y*/, float /*dx*/[2], float /*dy*/[2])
{
- return this->m_resizeMode;
}
- SocketReader *getReader();
+ private:
+ /* -------------------------------------------------------------------- */
+ /** \name Full Frame Methods
+ * \{ */
+
+ void render_full_frame(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system);
+
+ void render_full_frame_fallback(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system);
+ void render_tile(MemoryBuffer *output_buf, rcti *tile_rect);
+ Vector<NodeOperationOutput *> replace_inputs_with_buffers(Span<MemoryBuffer *> inputs_bufs);
+ void remove_buffers_and_restore_original_inputs(
+ Span<NodeOperationOutput *> original_inputs_links);
+
+ /** \} */
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+ /* allow the DebugInfo class to look at internals */
+ friend class DebugInfo;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
#endif
};
-class NodeOperationOutput {
- private:
- NodeOperation *m_operation;
-
- /** Datatype of this socket. Is used for automatically data transformation.
- * \section data-conversion
- */
- DataType m_datatype;
-
- public:
- NodeOperationOutput(NodeOperation *op, DataType datatype);
+std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operation_flags);
+std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation);
- NodeOperation &getOperation() const
- {
- return *m_operation;
- }
- DataType getDataType() const
- {
- return m_datatype;
- }
-
- /**
- * \brief determine the resolution of this data going through this socket
- * \param resolution: the result of this operation
- * \param preferredResolution: the preferable resolution as no resolution could be determined
- */
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
-
-#ifdef WITH_CXX_GUARDEDALLOC
- MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
-#endif
-};
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
index 1c741283c7b..1beb83bb477 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
@@ -38,24 +38,20 @@
#include "COM_NodeOperationBuilder.h" /* own include */
+namespace blender::compositor {
+
NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree)
: m_context(context), m_current_node(nullptr), m_active_viewer(nullptr)
{
m_graph.from_bNodeTree(*context, b_nodetree);
}
-NodeOperationBuilder::~NodeOperationBuilder()
-{
-}
-
void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
{
/* interface handle for nodes */
NodeConverter converter(this);
- for (int index = 0; index < m_graph.nodes().size(); index++) {
- Node *node = (Node *)m_graph.nodes()[index];
-
+ for (Node *node : m_graph.nodes()) {
m_current_node = node;
DebugInfo::node_to_operations(node);
@@ -69,7 +65,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
* so multiple operations can use the same node input.
*/
blender::MultiValueMap<NodeInput *, NodeOperationInput *> inverse_input_map;
- for (blender::Map<NodeOperationInput *, NodeInput *>::MutableItem item : m_input_map.items()) {
+ for (Map<NodeOperationInput *, NodeInput *>::MutableItem item : m_input_map.items()) {
inverse_input_map.add(item.value, item.key);
}
@@ -103,8 +99,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
determineResolutions();
- /* surround complex ops with read/write buffer */
- add_complex_operation_buffers();
+ if (m_context->get_execution_model() == eExecutionModel::Tiled) {
+ /* surround complex ops with read/write buffer */
+ add_complex_operation_buffers();
+ }
/* links not available from here on */
/* XXX make m_links a local variable to avoid confusion! */
@@ -115,8 +113,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
/* ensure topological (link-based) order of nodes */
/*sort_operations();*/ /* not needed yet */
- /* create execution groups */
- group_operations();
+ if (m_context->get_execution_model() == eExecutionModel::Tiled) {
+ /* create execution groups */
+ group_operations();
+ }
/* transfer resulting operations to the system */
system->set_operations(m_operations, m_groups);
@@ -124,7 +124,12 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
void NodeOperationBuilder::addOperation(NodeOperation *operation)
{
+ operation->set_id(m_operations.size());
m_operations.append(operation);
+ if (m_current_node) {
+ operation->set_name(m_current_node->getbNode()->name);
+ }
+ operation->set_execution_model(m_context->get_execution_model());
}
void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket,
@@ -251,12 +256,13 @@ void NodeOperationBuilder::registerViewer(ViewerOperation *viewer)
void NodeOperationBuilder::add_datatype_conversions()
{
- blender::Vector<Link> convert_links;
+ Vector<Link> convert_links;
for (const Link &link : m_links) {
/* proxy operations can skip data type conversion */
NodeOperation *from_op = &link.from()->getOperation();
NodeOperation *to_op = &link.to()->getOperation();
- if (!(from_op->useDatatypeConversion() || to_op->useDatatypeConversion())) {
+ if (!(from_op->get_flags().use_datatype_conversion ||
+ to_op->get_flags().use_datatype_conversion)) {
continue;
}
@@ -281,7 +287,7 @@ void NodeOperationBuilder::add_operation_input_constants()
/* Note: unconnected inputs cached first to avoid modifying
* m_operations while iterating over it
*/
- blender::Vector<NodeOperationInput *> pending_inputs;
+ Vector<NodeOperationInput *> pending_inputs;
for (NodeOperation *op : m_operations) {
for (int k = 0; k < op->getNumberOfInputSockets(); ++k) {
NodeOperationInput *input = op->getInputSocket(k);
@@ -349,11 +355,11 @@ void NodeOperationBuilder::add_input_constant_value(NodeOperationInput *input,
void NodeOperationBuilder::resolve_proxies()
{
- blender::Vector<Link> proxy_links;
+ Vector<Link> proxy_links;
for (const Link &link : m_links) {
/* don't replace links from proxy to proxy, since we may need them for replacing others! */
- if (link.from()->getOperation().isProxyOperation() &&
- !link.to()->getOperation().isProxyOperation()) {
+ if (link.from()->getOperation().get_flags().is_proxy_operation &&
+ !link.to()->getOperation().get_flags().is_proxy_operation) {
proxy_links.append(link);
}
}
@@ -364,7 +370,7 @@ void NodeOperationBuilder::resolve_proxies()
do {
/* walk upstream bypassing the proxy operation */
from = from->getOperation().getInputSocket(0)->getLink();
- } while (from && from->getOperation().isProxyOperation());
+ } while (from && from->getOperation().get_flags().is_proxy_operation);
removeInputLink(to);
/* we may not have a final proxy input link,
@@ -380,7 +386,7 @@ void NodeOperationBuilder::determineResolutions()
{
/* determine all resolutions of the operations (Width/Height) */
for (NodeOperation *op : m_operations) {
- if (op->isOutputOperation(m_context->isRendering()) && !op->isPreviewOperation()) {
+ if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) {
unsigned int resolution[2] = {0, 0};
unsigned int preferredResolution[2] = {0, 0};
op->determineResolution(resolution, preferredResolution);
@@ -389,7 +395,7 @@ void NodeOperationBuilder::determineResolutions()
}
for (NodeOperation *op : m_operations) {
- if (op->isOutputOperation(m_context->isRendering()) && op->isPreviewOperation()) {
+ if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) {
unsigned int resolution[2] = {0, 0};
unsigned int preferredResolution[2] = {0, 0};
op->determineResolution(resolution, preferredResolution);
@@ -399,9 +405,9 @@ void NodeOperationBuilder::determineResolutions()
/* add convert resolution operations when needed */
{
- blender::Vector<Link> convert_links;
+ Vector<Link> convert_links;
for (const Link &link : m_links) {
- if (link.to()->getResizeMode() != COM_SC_NO_RESIZE) {
+ if (link.to()->getResizeMode() != ResizeMode::None) {
NodeOperation &from_op = link.from()->getOperation();
NodeOperation &to_op = link.to()->getOperation();
if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) {
@@ -415,10 +421,10 @@ void NodeOperationBuilder::determineResolutions()
}
}
-blender::Vector<NodeOperationInput *> NodeOperationBuilder::cache_output_links(
+Vector<NodeOperationInput *> NodeOperationBuilder::cache_output_links(
NodeOperationOutput *output) const
{
- blender::Vector<NodeOperationInput *> inputs;
+ Vector<NodeOperationInput *> inputs;
for (const Link &link : m_links) {
if (link.from() == output) {
inputs.append(link.to());
@@ -433,7 +439,7 @@ WriteBufferOperation *NodeOperationBuilder::find_attached_write_buffer_operation
for (const Link &link : m_links) {
if (link.from() == output) {
NodeOperation &op = link.to()->getOperation();
- if (op.isWriteBufferOperation()) {
+ if (op.get_flags().is_write_buffer_operation) {
return (WriteBufferOperation *)(&op);
}
}
@@ -449,7 +455,7 @@ void NodeOperationBuilder::add_input_buffers(NodeOperation * /*operation*/,
}
NodeOperationOutput *output = input->getLink();
- if (output->getOperation().isReadBufferOperation()) {
+ if (output->getOperation().get_flags().is_read_buffer_operation) {
/* input is already buffered, no need to add another */
return;
}
@@ -483,7 +489,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation,
NodeOperationOutput *output)
{
/* cache connected sockets, so we can safely remove links first before replacing them */
- blender::Vector<NodeOperationInput *> targets = cache_output_links(output);
+ Vector<NodeOperationInput *> targets = cache_output_links(output);
if (targets.is_empty()) {
return;
}
@@ -491,7 +497,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation,
WriteBufferOperation *writeOperation = nullptr;
for (NodeOperationInput *target : targets) {
/* try to find existing write buffer operation */
- if (target->getOperation().isWriteBufferOperation()) {
+ if (target->getOperation().get_flags().is_write_buffer_operation) {
BLI_assert(writeOperation == nullptr); /* there should only be one write op connected */
writeOperation = (WriteBufferOperation *)(&target->getOperation());
}
@@ -534,9 +540,9 @@ void NodeOperationBuilder::add_complex_operation_buffers()
/* note: complex ops and get cached here first, since adding operations
* will invalidate iterators over the main m_operations
*/
- blender::Vector<NodeOperation *> complex_ops;
+ Vector<NodeOperation *> complex_ops;
for (NodeOperation *operation : m_operations) {
- if (operation->isComplex()) {
+ if (operation->get_flags().complex) {
complex_ops.append(operation);
}
}
@@ -571,7 +577,7 @@ static void find_reachable_operations_recursive(Tags &reachable, NodeOperation *
}
/* associated write-buffer operations are executed as well */
- if (op->isReadBufferOperation()) {
+ if (op->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read_op = (ReadBufferOperation *)op;
MemoryProxy *memproxy = read_op->getMemoryProxy();
find_reachable_operations_recursive(reachable, memproxy->getWriteBufferOperation());
@@ -589,7 +595,7 @@ void NodeOperationBuilder::prune_operations()
}
/* delete unreachable operations */
- blender::Vector<NodeOperation *> reachable_ops;
+ Vector<NodeOperation *> reachable_ops;
for (NodeOperation *op : m_operations) {
if (reachable.find(op) != reachable.end()) {
reachable_ops.append(op);
@@ -603,7 +609,7 @@ void NodeOperationBuilder::prune_operations()
}
/* topological (depth-first) sorting of operations */
-static void sort_operations_recursive(blender::Vector<NodeOperation *> &sorted,
+static void sort_operations_recursive(Vector<NodeOperation *> &sorted,
Tags &visited,
NodeOperation *op)
{
@@ -624,7 +630,7 @@ static void sort_operations_recursive(blender::Vector<NodeOperation *> &sorted,
void NodeOperationBuilder::sort_operations()
{
- blender::Vector<NodeOperation *> sorted;
+ Vector<NodeOperation *> sorted;
sorted.reserve(m_operations.size());
Tags visited;
@@ -657,7 +663,7 @@ static void add_group_operations_recursive(Tags &visited, NodeOperation *op, Exe
ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op)
{
- ExecutionGroup *group = new ExecutionGroup();
+ ExecutionGroup *group = new ExecutionGroup(this->m_groups.size());
m_groups.append(group);
Tags visited;
@@ -675,7 +681,7 @@ void NodeOperationBuilder::group_operations()
}
/* add new groups for associated memory proxies where needed */
- if (op->isReadBufferOperation()) {
+ if (op->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read_op = (ReadBufferOperation *)op;
MemoryProxy *memproxy = read_op->getMemoryProxy();
@@ -686,3 +692,42 @@ void NodeOperationBuilder::group_operations()
}
}
}
+
+/** Create a graphviz representation of the NodeOperationBuilder. */
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder)
+{
+ os << "# Builder start\n";
+ os << "digraph G {\n";
+ os << " rankdir=LR;\n";
+ os << " node [shape=box];\n";
+ for (const NodeOperation *operation : builder.get_operations()) {
+ os << " op" << operation->get_id() << " [label=\"" << *operation << "\"];\n";
+ }
+
+ os << "\n";
+ for (const NodeOperationBuilder::Link &link : builder.get_links()) {
+ os << " op" << link.from()->getOperation().get_id() << " -> op"
+ << link.to()->getOperation().get_id() << ";\n";
+ }
+ for (const NodeOperation *operation : builder.get_operations()) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ const ReadBufferOperation &read_operation = static_cast<const ReadBufferOperation &>(
+ *operation);
+ const WriteBufferOperation &write_operation =
+ *read_operation.getMemoryProxy()->getWriteBufferOperation();
+ os << " op" << write_operation.get_id() << " -> op" << read_operation.get_id() << ";\n";
+ }
+ }
+
+ os << "}\n";
+ os << "# Builder end\n";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link)
+{
+ os << link.from()->getOperation().get_id() << " -> " << link.to()->getOperation().get_id();
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h
index 8f55574fa88..b2fb822af25 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h
@@ -24,6 +24,8 @@
#include "COM_NodeGraph.h"
+namespace blender::compositor {
+
class CompositorContext;
class Node;
@@ -66,14 +68,14 @@ class NodeOperationBuilder {
const CompositorContext *m_context;
NodeGraph m_graph;
- blender::Vector<NodeOperation *> m_operations;
- blender::Vector<Link> m_links;
- blender::Vector<ExecutionGroup *> m_groups;
+ Vector<NodeOperation *> m_operations;
+ Vector<Link> m_links;
+ Vector<ExecutionGroup *> m_groups;
/** Maps operation inputs to node inputs */
- blender::Map<NodeOperationInput *, NodeInput *> m_input_map;
+ Map<NodeOperationInput *, NodeInput *> m_input_map;
/** Maps node outputs to operation outputs */
- blender::Map<NodeOutput *, NodeOperationOutput *> m_output_map;
+ Map<NodeOutput *, NodeOperationOutput *> m_output_map;
Node *m_current_node;
@@ -85,7 +87,6 @@ class NodeOperationBuilder {
public:
NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree);
- ~NodeOperationBuilder();
const CompositorContext &context() const
{
@@ -117,6 +118,16 @@ class NodeOperationBuilder {
return m_active_viewer;
}
+ const Vector<NodeOperation *> &get_operations() const
+ {
+ return m_operations;
+ }
+
+ const Vector<Link> &get_links() const
+ {
+ return m_links;
+ }
+
protected:
/** Add datatype conversion where needed */
void add_datatype_conversions();
@@ -132,7 +143,7 @@ class NodeOperationBuilder {
void determineResolutions();
/** Helper function to store connected inputs for replacement */
- blender::Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;
+ Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;
/** Find a connected write buffer operation to an OpOutput */
WriteBufferOperation *find_attached_write_buffer_operation(NodeOperationOutput *output) const;
/** Add read/write buffer operations around complex operations */
@@ -157,3 +168,8 @@ class NodeOperationBuilder {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompilerImpl")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder);
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc
index 4ac6bd50380..0f6ed0dbd2b 100644
--- a/source/blender/compositor/intern/COM_OpenCLDevice.cc
+++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc
@@ -19,6 +19,8 @@
#include "COM_OpenCLDevice.h"
#include "COM_WorkScheduler.h"
+namespace blender::compositor {
+
enum COM_VendorID { NVIDIA = 0x10DE, AMD = 0x1002 };
const cl_image_format IMAGE_FORMAT_COLOR = {
CL_RGBA,
@@ -48,6 +50,16 @@ OpenCLDevice::OpenCLDevice(cl_context context,
this->m_queue = clCreateCommandQueue(this->m_context, this->m_device, 0, &error);
}
+OpenCLDevice::OpenCLDevice(OpenCLDevice &&other) noexcept
+ : m_context(other.m_context),
+ m_device(other.m_device),
+ m_program(other.m_program),
+ m_queue(other.m_queue),
+ m_vendorID(other.m_vendorID)
+{
+ other.m_queue = nullptr;
+}
+
OpenCLDevice::~OpenCLDevice()
{
if (this->m_queue) {
@@ -55,18 +67,16 @@ OpenCLDevice::~OpenCLDevice()
}
}
-void OpenCLDevice::execute(WorkPackage *work)
+void OpenCLDevice::execute(WorkPackage *work_package)
{
- const unsigned int chunkNumber = work->chunk_number;
- ExecutionGroup *executionGroup = work->execution_group;
- rcti rect;
+ const unsigned int chunkNumber = work_package->chunk_number;
+ ExecutionGroup *executionGroup = work_package->execution_group;
- executionGroup->determineChunkRect(&rect, chunkNumber);
MemoryBuffer **inputBuffers = executionGroup->getInputBuffersOpenCL(chunkNumber);
- MemoryBuffer *outputBuffer = executionGroup->allocateOutputBuffer(rect);
+ MemoryBuffer *outputBuffer = executionGroup->allocateOutputBuffer(work_package->rect);
executionGroup->getOutputOperation()->executeOpenCLRegion(
- this, &rect, chunkNumber, inputBuffers, outputBuffer);
+ this, &work_package->rect, chunkNumber, inputBuffers, outputBuffer);
delete outputBuffer;
@@ -270,3 +280,5 @@ cl_kernel OpenCLDevice::COM_clCreateKernel(const char *kernelname,
}
return kernel;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h
index 30d9a59d182..826b0457a49 100644
--- a/source/blender/compositor/intern/COM_OpenCLDevice.h
+++ b/source/blender/compositor/intern/COM_OpenCLDevice.h
@@ -25,6 +25,8 @@ class OpenCLDevice;
#include "COM_WorkScheduler.h"
#include "clew.h"
+namespace blender::compositor {
+
/**
* \brief device representing an GPU OpenCL device.
* an instance of this class represents a single cl_device
@@ -65,6 +67,9 @@ class OpenCLDevice : public Device {
* \param vendorID:
*/
OpenCLDevice(cl_context context, cl_device_id device, cl_program program, cl_int vendorId);
+
+ OpenCLDevice(OpenCLDevice &&other) noexcept;
+
~OpenCLDevice();
/**
@@ -117,3 +122,5 @@ class OpenCLDevice : public Device {
NodeOperation *operation);
cl_kernel COM_clCreateKernel(const char *kernelname, std::list<cl_kernel> *clKernelsToCleanUp);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
new file mode 100644
index 00000000000..7e0486b0f54
--- /dev/null
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
@@ -0,0 +1,129 @@
+/*
+ * 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_SharedOperationBuffers.h"
+#include "BLI_rect.h"
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+SharedOperationBuffers::BufferData::BufferData()
+ : buffer(nullptr), registered_reads(0), received_reads(0), is_rendered(false)
+{
+}
+
+SharedOperationBuffers::BufferData &SharedOperationBuffers::get_buffer_data(NodeOperation *op)
+{
+ return buffers_.lookup_or_add_cb(op, []() { return BufferData(); });
+}
+
+/**
+ * Whether given operation area to render is already registered.
+ * TODO: Possibly refactor to "request_area". Current implementation is incomplete: partial
+ * overlapping, etc. Leading to more rendering than necessary.
+ */
+bool SharedOperationBuffers::is_area_registered(NodeOperation *op, const rcti &area_to_render)
+{
+ BufferData &buf_data = get_buffer_data(op);
+ for (rcti &reg_rect : buf_data.render_areas) {
+ if (BLI_rcti_inside_rcti(&reg_rect, &area_to_render)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Registers an operation area to render.
+ */
+void SharedOperationBuffers::register_area(NodeOperation *op, const rcti &area_to_render)
+{
+ get_buffer_data(op).render_areas.append(area_to_render);
+}
+
+/**
+ * Whether given operation has any registered reads (other operation registered it depends on given
+ * operation).
+ */
+bool SharedOperationBuffers::has_registered_reads(NodeOperation *op)
+{
+ return get_buffer_data(op).registered_reads > 0;
+}
+
+/**
+ * Registers an operation read (other operation depends on given operation).
+ */
+void SharedOperationBuffers::register_read(NodeOperation *read_op)
+{
+ get_buffer_data(read_op).registered_reads++;
+}
+
+/**
+ * Get registered areas given operation needs to render.
+ */
+blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op)
+{
+ return get_buffer_data(op).render_areas.as_span();
+}
+
+/**
+ * Whether this operation buffer has already been rendered.
+ */
+bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op)
+{
+ return get_buffer_data(op).is_rendered;
+}
+
+/**
+ * Stores given operation rendered buffer.
+ */
+void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op,
+ std::unique_ptr<MemoryBuffer> buffer)
+{
+ BufferData &buf_data = get_buffer_data(op);
+ BLI_assert(buf_data.received_reads == 0);
+ BLI_assert(buf_data.buffer == nullptr);
+ buf_data.buffer = std::move(buffer);
+ buf_data.is_rendered = true;
+}
+
+/**
+ * Get given operation rendered buffer.
+ */
+MemoryBuffer *SharedOperationBuffers::get_rendered_buffer(NodeOperation *op)
+{
+ BLI_assert(is_operation_rendered(op));
+ return get_buffer_data(op).buffer.get();
+}
+
+/**
+ * Reports an operation has finished reading given operation. If all given operation dependencies
+ * have finished its buffer will be disposed.
+ */
+void SharedOperationBuffers::read_finished(NodeOperation *read_op)
+{
+ BufferData &buf_data = get_buffer_data(read_op);
+ buf_data.received_reads++;
+ BLI_assert(buf_data.received_reads > 0 && buf_data.received_reads <= buf_data.registered_reads);
+ if (buf_data.received_reads == buf_data.registered_reads) {
+ /* Dispose buffer. */
+ buf_data.buffer = nullptr;
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
new file mode 100644
index 00000000000..f7763cd8ae4
--- /dev/null
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
@@ -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.
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
+#include "COM_MemoryBuffer.h"
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+#include <memory>
+
+namespace blender::compositor {
+
+/**
+ * Stores and shares operations rendered buffers including render data. Buffers are
+ * disposed once all dependent operations have finished reading them.
+ */
+class SharedOperationBuffers {
+ private:
+ typedef struct BufferData {
+ public:
+ BufferData();
+ std::unique_ptr<MemoryBuffer> buffer;
+ blender::Vector<rcti> render_areas;
+ int registered_reads;
+ int received_reads;
+ bool is_rendered;
+ } BufferData;
+ blender::Map<NodeOperation *, BufferData> buffers_;
+
+ public:
+ bool is_area_registered(NodeOperation *op, const rcti &area_to_render);
+ void register_area(NodeOperation *op, const rcti &area_to_render);
+
+ bool has_registered_reads(NodeOperation *op);
+ void register_read(NodeOperation *read_op);
+
+ blender::Span<rcti> get_areas_to_render(NodeOperation *op);
+ bool is_operation_rendered(NodeOperation *op);
+ void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer);
+ MemoryBuffer *get_rendered_buffer(NodeOperation *op);
+
+ void read_finished(NodeOperation *read_op);
+
+ private:
+ BufferData &get_buffer_data(NodeOperation *op);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:SharedOperationBuffers")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
index 5febf3802de..01be6e1afed 100644
--- a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
+++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
@@ -18,10 +18,13 @@
#include "COM_SingleThreadedOperation.h"
+namespace blender::compositor {
+
SingleThreadedOperation::SingleThreadedOperation()
{
this->m_cachedInstance = nullptr;
- setComplex(true);
+ flags.complex = true;
+ flags.single_threaded = true;
}
void SingleThreadedOperation::initExecution()
@@ -56,3 +59,5 @@ void *SingleThreadedOperation::initializeTileData(rcti *rect)
unlockMutex();
return this->m_cachedInstance;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.h b/source/blender/compositor/intern/COM_SingleThreadedOperation.h
index 82326b57b57..9945f938ff9 100644
--- a/source/blender/compositor/intern/COM_SingleThreadedOperation.h
+++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SingleThreadedOperation : public NodeOperation {
private:
MemoryBuffer *m_cachedInstance;
@@ -51,9 +53,6 @@ class SingleThreadedOperation : public NodeOperation {
void *initializeTileData(rcti *rect) override;
virtual MemoryBuffer *createMemoryBuffer(rcti *rect) = 0;
-
- int isSingleThreaded() override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SocketReader.h b/source/blender/compositor/intern/COM_SocketReader.h
deleted file mode 100644
index 7c4132efe60..00000000000
--- a/source/blender/compositor/intern/COM_SocketReader.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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 2011, Blender Foundation.
- */
-
-#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
-
-typedef enum PixelSampler {
- COM_PS_NEAREST = 0,
- COM_PS_BILINEAR = 1,
- COM_PS_BICUBIC = 2,
-} PixelSampler;
-
-class MemoryBuffer;
-
-/**
- * \brief Helper class for reading socket data.
- * Only use this class for dispatching (un-ary and n-ary) executions.
- * \ingroup Execution
- */
-class SocketReader {
- private:
- protected:
- /**
- * \brief Holds the width of the output of this operation.
- */
- unsigned int m_width;
-
- /**
- * \brief Holds the height of the output of this operation.
- */
- unsigned int m_height;
-
- /**
- * \brief calculate a single pixel
- * \note this method is called for non-complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- */
- virtual void executePixelSampled(float /*output*/[4],
- float /*x*/,
- float /*y*/,
- PixelSampler /*sampler*/)
- {
- }
-
- /**
- * \brief calculate a single pixel
- * \note this method is called for complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- * \param chunkData: chunk specific data a during execution time.
- */
- virtual void executePixel(float output[4], int x, int y, void * /*chunkData*/)
- {
- executePixelSampled(output, x, y, COM_PS_NEAREST);
- }
-
- /**
- * \brief calculate a single pixel using an EWA filter
- * \note this method is called for complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param dx:
- * \param dy:
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- */
- virtual void executePixelFiltered(
- float /*output*/[4], float /*x*/, float /*y*/, float /*dx*/[2], float /*dy*/[2])
- {
- }
-
- public:
- inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
- {
- executePixelSampled(result, x, y, sampler);
- }
- inline void read(float result[4], int x, int y, void *chunkData)
- {
- executePixel(result, x, y, chunkData);
- }
- inline void readFiltered(float result[4], float x, float y, float dx[2], float dy[2])
- {
- executePixelFiltered(result, x, y, dx, dy);
- }
-
- virtual void *initializeTileData(rcti * /*rect*/)
- {
- return 0;
- }
- virtual void deinitializeTileData(rcti * /*rect*/, void * /*data*/)
- {
- }
-
- virtual ~SocketReader()
- {
- }
-
- virtual MemoryBuffer *getInputMemoryBuffer(MemoryBuffer ** /*memoryBuffers*/)
- {
- return 0;
- }
-
- inline unsigned int getWidth() const
- {
- return this->m_width;
- }
- inline unsigned int getHeight() const
- {
- 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/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc
new file mode 100644
index 00000000000..d025ce53330
--- /dev/null
+++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc
@@ -0,0 +1,158 @@
+/*
+ * 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_TiledExecutionModel.h"
+#include "COM_Debug.h"
+#include "COM_ExecutionGroup.h"
+#include "COM_ReadBufferOperation.h"
+#include "COM_WorkScheduler.h"
+
+#include "BLT_translation.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+TiledExecutionModel::TiledExecutionModel(CompositorContext &context,
+ Span<NodeOperation *> operations,
+ Span<ExecutionGroup *> groups)
+ : ExecutionModel(context, operations), groups_(groups)
+{
+ const bNodeTree *node_tree = context.getbNodeTree();
+ node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution"));
+
+ unsigned int resolution[2];
+ for (ExecutionGroup *group : groups_) {
+ resolution[0] = 0;
+ resolution[1] = 0;
+ group->determineResolution(resolution);
+
+ if (border_.use_render_border) {
+ const rctf *render_border = border_.viewer_border;
+ group->setRenderBorder(
+ render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax);
+ }
+
+ if (border_.use_viewer_border) {
+ const rctf *viewer_border = border_.viewer_border;
+ group->setViewerBorder(
+ viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax);
+ }
+ }
+}
+
+static void update_read_buffer_offset(Span<NodeOperation *> operations)
+{
+ unsigned int order = 0;
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
+ readOperation->setOffset(order);
+ order++;
+ }
+ }
+}
+
+static void init_write_operations_for_execution(Span<NodeOperation *> operations,
+ const bNodeTree *bTree)
+{
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_write_buffer_operation) {
+ operation->setbNodeTree(bTree);
+ operation->initExecution();
+ }
+ }
+}
+
+static void link_write_buffers(Span<NodeOperation *> operations)
+{
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
+ readOperation->updateMemoryBuffer();
+ }
+ }
+}
+
+static void init_non_write_operations_for_execution(Span<NodeOperation *> operations,
+ const bNodeTree *bTree)
+{
+ for (NodeOperation *operation : operations) {
+ if (!operation->get_flags().is_write_buffer_operation) {
+ operation->setbNodeTree(bTree);
+ operation->initExecution();
+ }
+ }
+}
+
+static void init_execution_groups_for_execution(Span<ExecutionGroup *> groups,
+ const int chunk_size)
+{
+ for (ExecutionGroup *execution_group : groups) {
+ execution_group->setChunksize(chunk_size);
+ execution_group->initExecution();
+ }
+}
+
+void TiledExecutionModel::execute(ExecutionSystem &exec_system)
+{
+ const bNodeTree *editingtree = this->context_.getbNodeTree();
+
+ editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution"));
+
+ update_read_buffer_offset(operations_);
+
+ init_write_operations_for_execution(operations_, context_.getbNodeTree());
+ link_write_buffers(operations_);
+ init_non_write_operations_for_execution(operations_, context_.getbNodeTree());
+ init_execution_groups_for_execution(groups_, context_.getChunksize());
+
+ WorkScheduler::start(context_);
+ execute_groups(eCompositorPriority::High, exec_system);
+ if (!context_.isFastCalculation()) {
+ execute_groups(eCompositorPriority::Medium, exec_system);
+ execute_groups(eCompositorPriority::Low, exec_system);
+ }
+ WorkScheduler::finish();
+ WorkScheduler::stop();
+
+ editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution"));
+
+ for (NodeOperation *operation : operations_) {
+ operation->deinitExecution();
+ }
+
+ for (ExecutionGroup *execution_group : groups_) {
+ execution_group->deinitExecution();
+ }
+}
+
+void TiledExecutionModel::execute_groups(eCompositorPriority priority,
+ ExecutionSystem &exec_system)
+{
+ for (ExecutionGroup *execution_group : groups_) {
+ if (execution_group->get_flags().is_output &&
+ execution_group->getRenderPriority() == priority) {
+ execution_group->execute(&exec_system);
+ }
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h
new file mode 100644
index 00000000000..05a795b9f07
--- /dev/null
+++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h
@@ -0,0 +1,54 @@
+/*
+ * 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 "COM_ExecutionModel.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+class ExecutionGroup;
+
+/**
+ * Operations are executed from outputs to inputs grouped in execution groups and rendered in
+ * tiles.
+ */
+class TiledExecutionModel : public ExecutionModel {
+ private:
+ Span<ExecutionGroup *> groups_;
+
+ public:
+ TiledExecutionModel(CompositorContext &context,
+ Span<NodeOperation *> operations,
+ Span<ExecutionGroup *> groups);
+
+ void execute(ExecutionSystem &exec_system) override;
+
+ private:
+ void execute_groups(eCompositorPriority priority, ExecutionSystem &exec_system);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:TiledExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkPackage.cc b/source/blender/compositor/intern/COM_WorkPackage.cc
index 60684f2c45c..ea78c0d6333 100644
--- a/source/blender/compositor/intern/COM_WorkPackage.cc
+++ b/source/blender/compositor/intern/COM_WorkPackage.cc
@@ -18,8 +18,20 @@
#include "COM_WorkPackage.h"
-WorkPackage::WorkPackage(ExecutionGroup *execution_group, unsigned int chunk_number)
+#include "COM_Enums.h"
+#include "COM_ExecutionGroup.h"
+
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const WorkPackage &work_package)
{
- this->execution_group = execution_group;
- this->chunk_number = chunk_number;
+ os << "WorkPackage(execution_group=" << *work_package.execution_group;
+ os << ",chunk=" << work_package.chunk_number;
+ os << ",state=" << work_package.state;
+ os << ",rect=(" << work_package.rect.xmin << "," << work_package.rect.ymin << ")-("
+ << work_package.rect.xmax << "," << work_package.rect.ymax << ")";
+ os << ")";
+ return os;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h
index db5eb3d3072..f0f53f300a5 100644
--- a/source/blender/compositor/intern/COM_WorkPackage.h
+++ b/source/blender/compositor/intern/COM_WorkPackage.h
@@ -18,14 +18,30 @@
#pragma once
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+#include "COM_Enums.h"
+
+#include "BLI_rect.h"
+
+#include <functional>
+#include <ostream>
+
+namespace blender::compositor {
+// Forward Declarations.
class ExecutionGroup;
-#include "COM_ExecutionGroup.h"
/**
* \brief contains data about work that can be scheduled
* \see WorkScheduler
*/
struct WorkPackage {
+ eWorkPackageType type;
+
+ eWorkPackageState state = eWorkPackageState::NotScheduled;
+
/**
* \brief executionGroup with the operations-setup to be evaluated
*/
@@ -37,13 +53,25 @@ struct WorkPackage {
unsigned int chunk_number;
/**
- * constructor
- * \param group: the ExecutionGroup
- * \param chunk_number: the number of the chunk
+ * Area of the execution group that the work package calculates.
*/
- WorkPackage(ExecutionGroup *group, unsigned int chunk_number);
+ rcti rect;
+
+ /**
+ * Custom function to execute when work package type is CustomFunction.
+ */
+ std::function<void()> execute_fn;
+
+ /**
+ * Called when work execution is finished.
+ */
+ std::function<void()> executed_fn;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkPackage")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const WorkPackage &work_package);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc
index 7d9dd502762..cd0139fd18e 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.cc
+++ b/source/blender/compositor/intern/COM_WorkScheduler.cc
@@ -37,6 +37,8 @@
#include "BKE_global.h"
+namespace blender::compositor {
+
enum class ThreadingModel {
/** Everything is executed in the caller thread. easy for debugging. */
SingleThreaded,
@@ -70,7 +72,7 @@ static struct {
/** \brief list of all CPUDevices. for every hardware thread an instance of CPUDevice is
* created
*/
- blender::Vector<CPUDevice> devices;
+ Vector<CPUDevice> devices;
/** \brief list of all thread for every CPUDevice in cpudevices a thread exists. */
ListBase threads;
@@ -89,13 +91,15 @@ static struct {
cl_program program;
/** \brief list of all OpenCLDevices. for every OpenCL GPU device an instance of OpenCLDevice
* is created. */
- blender::Vector<OpenCLDevice> devices;
+ Vector<OpenCLDevice> devices;
/** \brief list of all thread for every GPUDevice in cpudevices a thread exists. */
ListBase threads;
/** \brief all scheduled work for the GPU. */
bool active = false;
bool initialized = false;
} opencl;
+
+ int num_cpu_threads;
} g_work_scheduler;
/* -------------------------------------------------------------------- */
@@ -117,7 +121,6 @@ static void *thread_execute_gpu(void *data)
while ((work = (WorkPackage *)BLI_thread_queue_pop(g_work_scheduler.opencl.queue))) {
device->execute(work);
- delete work;
}
return nullptr;
@@ -142,7 +145,8 @@ static void opencl_start(CompositorContext &context)
static bool opencl_schedule(WorkPackage *package)
{
- if (package->execution_group->isOpenCL() && g_work_scheduler.opencl.active) {
+ if (package->type == eWorkPackageType::Tile && package->execution_group->get_flags().open_cl &&
+ g_work_scheduler.opencl.active) {
BLI_thread_queue_push(g_work_scheduler.opencl.queue, package);
return true;
}
@@ -262,10 +266,10 @@ static void opencl_initialize(const bool use_opencl)
if (error2 != CL_SUCCESS) {
printf("CLERROR[%d]: %s\n", error2, clewErrorString(error2));
}
- g_work_scheduler.opencl.devices.append(OpenCLDevice(g_work_scheduler.opencl.context,
- device,
- g_work_scheduler.opencl.program,
- vendorID));
+ g_work_scheduler.opencl.devices.append_as(g_work_scheduler.opencl.context,
+ device,
+ g_work_scheduler.opencl.program,
+ vendorID);
}
}
MEM_freeN(cldevices);
@@ -304,7 +308,6 @@ static void threading_model_single_thread_execute(WorkPackage *package)
{
CPUDevice device(0);
device.execute(package);
- delete package;
}
/* \} */
@@ -320,7 +323,6 @@ static void *threading_model_queue_execute(void *data)
BLI_thread_local_set(g_thread_device, device);
while ((work = (WorkPackage *)BLI_thread_queue_pop(g_work_scheduler.queue.queue))) {
device->execute(work);
- delete work;
}
return nullptr;
@@ -369,7 +371,7 @@ static void threading_model_queue_initialize(const int num_cpu_threads)
/* Initialize CPU threads. */
if (!g_work_scheduler.queue.initialized) {
for (int index = 0; index < num_cpu_threads; index++) {
- g_work_scheduler.queue.devices.append(CPUDevice(index));
+ g_work_scheduler.queue.devices.append_as(index);
}
BLI_thread_local_create(g_thread_device);
g_work_scheduler.queue.initialized = true;
@@ -398,7 +400,6 @@ static void threading_model_task_execute(TaskPool *__restrict UNUSED(pool), void
CPUDevice device(BLI_task_parallel_thread_id(nullptr));
BLI_thread_local_set(g_thread_device, &device);
device.execute(package);
- delete package;
}
static void threading_model_task_schedule(WorkPackage *package)
@@ -410,7 +411,8 @@ static void threading_model_task_schedule(WorkPackage *package)
static void threading_model_task_start()
{
BLI_thread_local_create(g_thread_device);
- g_work_scheduler.task.pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH);
+ g_work_scheduler.task.pool = BLI_task_pool_create(
+ nullptr, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
static void threading_model_task_finish()
@@ -431,10 +433,8 @@ static void threading_model_task_stop()
/** \name Public API
* \{ */
-void WorkScheduler::schedule(ExecutionGroup *group, int chunkNumber)
+void WorkScheduler::schedule(WorkPackage *package)
{
- WorkPackage *package = new WorkPackage(group, chunkNumber);
-
if (COM_is_opencl_enabled()) {
if (opencl_schedule(package)) {
return;
@@ -536,11 +536,12 @@ void WorkScheduler::initialize(bool use_opencl, int num_cpu_threads)
opencl_initialize(use_opencl);
}
+ g_work_scheduler.num_cpu_threads = num_cpu_threads;
switch (COM_threading_model()) {
case ThreadingModel::SingleThreaded:
+ g_work_scheduler.num_cpu_threads = 1;
/* Nothing to do. */
break;
-
case ThreadingModel::Queue:
threading_model_queue_initialize(num_cpu_threads);
break;
@@ -572,6 +573,11 @@ void WorkScheduler::deinitialize()
}
}
+int WorkScheduler::get_num_cpu_threads()
+{
+ return g_work_scheduler.num_cpu_threads;
+}
+
int WorkScheduler::current_thread_id()
{
if (COM_threading_model() == ThreadingModel::SingleThreaded) {
@@ -583,3 +589,5 @@ int WorkScheduler::current_thread_id()
}
/* \} */
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h
index 6b53cc3efd6..be88859be7c 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.h
+++ b/source/blender/compositor/intern/COM_WorkScheduler.h
@@ -24,6 +24,8 @@
#include "COM_WorkPackage.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
/** \brief the workscheduler
* \ingroup execution
*/
@@ -31,13 +33,11 @@ struct WorkScheduler {
/**
* \brief schedule a chunk of a group to be calculated.
* An execution group schedules a chunk in the WorkScheduler
- * when ExecutionGroup.isOpenCL is set the work will be handled by a OpenCLDevice
+ * when ExecutionGroup.get_flags().open_cl is set the work will be handled by a OpenCLDevice
* otherwise the work is scheduled for an CPUDevice
* \see ExecutionGroup.execute
- * \param group: the execution group
- * \param chunkNumber: the number of the chunk in the group to be executed
*/
- static void schedule(ExecutionGroup *group, int chunkNumber);
+ static void schedule(WorkPackage *package);
/**
* \brief initialize the WorkScheduler
@@ -87,9 +87,13 @@ struct WorkScheduler {
*/
static bool has_gpu_devices();
+ static int get_num_cpu_threads();
+
static int current_thread_id();
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkScheduler")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc
index 68e4f80f91f..5839f53976b 100644
--- a/source/blender/compositor/intern/COM_compositor.cc
+++ b/source/blender/compositor/intern/COM_compositor.cc
@@ -46,12 +46,12 @@ static void compositor_init_node_previews(const RenderData *render_data, bNodeTr
1.0f;
int preview_width, preview_height;
if (aspect < 1.0f) {
- preview_width = COM_PREVIEW_SIZE;
- preview_height = (int)(COM_PREVIEW_SIZE * aspect);
+ preview_width = blender::compositor::COM_PREVIEW_SIZE;
+ preview_height = (int)(blender::compositor::COM_PREVIEW_SIZE * aspect);
}
else {
- preview_width = (int)(COM_PREVIEW_SIZE / aspect);
- preview_height = COM_PREVIEW_SIZE;
+ preview_width = (int)(blender::compositor::COM_PREVIEW_SIZE / aspect);
+ preview_height = blender::compositor::COM_PREVIEW_SIZE;
}
BKE_node_preview_init_tree(node_tree, preview_width, preview_height, false);
}
@@ -92,12 +92,12 @@ void COM_execute(RenderData *render_data,
/* Initialize workscheduler. */
const bool use_opencl = (node_tree->flag & NTREE_COM_OPENCL) != 0;
- WorkScheduler::initialize(use_opencl, BKE_render_num_threads(render_data));
+ blender::compositor::WorkScheduler::initialize(use_opencl, BKE_render_num_threads(render_data));
/* Execute. */
const bool twopass = (node_tree->flag & NTREE_TWO_PASS) && !rendering;
if (twopass) {
- ExecutionSystem fast_pass(
+ blender::compositor::ExecutionSystem fast_pass(
render_data, scene, node_tree, rendering, true, viewSettings, displaySettings, viewName);
fast_pass.execute();
@@ -107,7 +107,7 @@ void COM_execute(RenderData *render_data,
}
}
- ExecutionSystem system(
+ blender::compositor::ExecutionSystem system(
render_data, scene, node_tree, rendering, false, viewSettings, displaySettings, viewName);
system.execute();
@@ -118,7 +118,7 @@ void COM_deinitialize()
{
if (g_compositor.is_initialized) {
BLI_mutex_lock(&g_compositor.mutex);
- WorkScheduler::deinitialize();
+ blender::compositor::WorkScheduler::deinitialize();
g_compositor.is_initialized = false;
BLI_mutex_unlock(&g_compositor.mutex);
BLI_mutex_end(&g_compositor.mutex);