[ufo-core] 01/08: Imported Upstream version 0.3

Frédéric-Emmanuel Picca picca at moszumanska.debian.org
Sat Nov 30 20:54:11 UTC 2013


This is an automated email from the git hooks/post-receive script.

picca pushed a commit to branch master
in repository ufo-core.

commit a9dd653b28ac01b53af2920de2d904b0e6b39795
Author: Picca Frédéric-Emmanuel <picca at debian.org>
Date:   Sat Jul 27 09:00:13 2013 +0200

    Imported Upstream version 0.3
---
 .gitignore                                  |    1 +
 CMakeLists.txt                              |  119 +++
 COPYING                                     |  165 ++++
 common/cmake/FindFFTW3.cmake                |  133 ++++
 common/cmake/FindGObjectIntrospection.cmake |   61 ++
 common/cmake/FindNumpy.cmake                |   57 ++
 common/cmake/FindOCLFFT.cmake               |   14 +
 common/cmake/FindOpenCL.cmake               |   89 +++
 common/cmake/FindVala.cmake                 |   65 ++
 docs/CMakeLists.txt                         |   41 +
 docs/Ufo-docs.xml.in                        |   29 +
 docs/Ufo-sections.txt.in                    |  180 +++++
 docs/manual/_static/ufo-logo.png            |  Bin 0 -> 2512 bytes
 docs/manual/_templates/indexcontent.html    |   48 ++
 docs/manual/_templates/indexsidebar.html    |    5 +
 docs/manual/_templates/layout.html          |    5 +
 docs/manual/api/general.rst                 |   19 +
 docs/manual/api/index.rst                   |   13 +
 docs/manual/api/ufo-0.3.rst                 | 1093 +++++++++++++++++++++++++++
 docs/manual/bugs.rst                        |   18 +
 docs/manual/conf.py.in                      |  202 +++++
 docs/manual/contents.rst                    |   17 +
 docs/manual/copyright.rst                   |    8 +
 docs/manual/faq.rst                         |  153 ++++
 docs/manual/glossary.rst                    |   14 +
 docs/manual/install/index.rst               |   14 +
 docs/manual/install/linux.rst               |  175 +++++
 docs/manual/install/mac.rst                 |   69 ++
 docs/manual/json.rst                        |  144 ++++
 docs/manual/using/background.rst            |   86 +++
 docs/manual/using/cluster.rst               |   23 +
 docs/manual/using/filters.rst               |  244 ++++++
 docs/manual/using/index.rst                 |   30 +
 docs/manual/using/quickstart.rst            |  195 +++++
 docs/manual/whatsnew/0.1.rst                |   12 +
 docs/manual/whatsnew/0.2.rst                |   69 ++
 docs/manual/whatsnew/0.3.rst                |   31 +
 docs/manual/whatsnew/index.rst              |   15 +
 docs/scangobj.sh.in                         |    1 +
 python/CMakeLists.txt                       |   29 +
 python/setup.py.in                          |   13 +
 python/ufotools/__init__.py                 |   27 +
 tests/CMakeLists.txt                        |   33 +
 tests/gtester.xsl                           |   72 ++
 tests/test-config.c                         |   96 +++
 tests/test-graph.c                          |  283 +++++++
 tests/test-profiler.c                       |   76 ++
 tests/test-suite.c                          |   47 ++
 tests/test-suite.h                          |    8 +
 tests/test.json                             |   24 +
 tools/CMakeLists.txt                        |   13 +
 tools/clprof                                |  126 +++
 tools/deploy.sh                             |   91 +++
 tools/leakcheck.sh                          |    4 +
 tools/mkfilter.py                           |   87 +++
 tools/runjson.c                             |  135 ++++
 tools/templates/ufo-cpu-task.c.in           |  174 +++++
 tools/templates/ufo-cpu-task.h.in           |   66 ++
 tools/templates/ufo-gpu-task.c.in           |  181 +++++
 tools/templates/ufo-gpu-task.h.in           |   66 ++
 tools/ufod.c                                |  468 ++++++++++++
 ufo/CMakeLists.txt                          |  271 +++++++
 ufo/Ufo.types                               |    0
 ufo/config.h.in                             |    3 +
 ufo/ufo-arch-graph.c                        |  292 +++++++
 ufo/ufo-arch-graph.h                        |   78 ++
 ufo/ufo-buffer.c                            |  566 ++++++++++++++
 ufo/ufo-buffer.h                            |  161 ++++
 ufo/ufo-config.c                            |  281 +++++++
 ufo/ufo-config.h                            |   72 ++
 ufo/ufo-configurable.c                      |   53 ++
 ufo/ufo-configurable.h                      |   50 ++
 ufo/ufo-cpu-node.c                          |  121 +++
 ufo/ufo-cpu-node.h                          |   71 ++
 ufo/ufo-cpu-task-iface.c                    |   85 +++
 ufo/ufo-cpu-task-iface.h                    |   73 ++
 ufo/ufo-dummy-task.c                        |   88 +++
 ufo/ufo-dummy-task.h                        |   70 ++
 ufo/ufo-enums.c.template                    |   44 ++
 ufo/ufo-enums.h.template                    |   25 +
 ufo/ufo-gpu-node.c                          |  110 +++
 ufo/ufo-gpu-node.h                          |   71 ++
 ufo/ufo-gpu-task-iface.c                    |   90 +++
 ufo/ufo-gpu-task-iface.h                    |   80 ++
 ufo/ufo-graph.c                             |  744 ++++++++++++++++++
 ufo/ufo-graph.h                             |  123 +++
 ufo/ufo-group.c                             |  346 +++++++++
 ufo/ufo-group.h                             |  103 +++
 ufo/ufo-input-task.c                        |  204 +++++
 ufo/ufo-input-task.h                        |   74 ++
 ufo/ufo-node.c                              |  147 ++++
 ufo/ufo-node.h                              |   80 ++
 ufo/ufo-output-task.c                       |  244 ++++++
 ufo/ufo-output-task.h                       |   75 ++
 ufo/ufo-plugin-manager.c                    |  369 +++++++++
 ufo/ufo-plugin-manager.h                    |   86 +++
 ufo/ufo-profiler.c                          |  353 +++++++++
 ufo/ufo-profiler.h                          |  110 +++
 ufo/ufo-remote-node.c                       |  374 +++++++++
 ufo/ufo-remote-node.h                       |  133 ++++
 ufo/ufo-remote-task.c                       |  176 +++++
 ufo/ufo-remote-task.h                       |   70 ++
 ufo/ufo-resources.c                         |  691 +++++++++++++++++
 ufo/ufo-resources.h                         |  103 +++
 ufo/ufo-scheduler.c                         |  704 +++++++++++++++++
 ufo/ufo-scheduler.h                         |   80 ++
 ufo/ufo-task-graph.c                        |  846 +++++++++++++++++++++
 ufo/ufo-task-graph.h                        |  104 +++
 ufo/ufo-task-iface.c                        |   92 +++
 ufo/ufo-task-iface.h                        |  103 +++
 ufo/ufo-task-node.c                         |  240 ++++++
 ufo/ufo-task-node.h                         |   96 +++
 ufo/ufo.h                                   |   52 ++
 ufo/ufo.pc.in                               |   13 +
 114 files changed, 15466 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..567609b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..a608d96
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,119 @@
+cmake_minimum_required(VERSION 2.6)
+project(ufo C)
+
+set(TARNAME "libufo")
+set(UFO_VERSION_MAJOR "0")
+set(UFO_VERSION_MINOR "3")
+set(UFO_VERSION_PATCH "1")
+set(UFO_GIR_VERSION "${UFO_VERSION_MAJOR}.${UFO_VERSION_MINOR}")
+
+# increase UFO_SO_VERSION on each version that breaks ABI compatibility
+set(UFO_SO_VERSION "3")
+
+set(UFO_DESCRIPTION "UFO high-speed image processing core library")
+set(UFO_DESCRIPTION_SUMMARY "UFO high-speed image processing core library")
+
+set(PACKAGE_VERSION_MAJOR ${UFO_VERSION_MAJOR})
+set(PACKAGE_VERSION_MINOR ${UFO_VERSION_MINOR})
+set(PACKAGE_VERSION_PATCH ${UFO_VERSION_PATCH})
+set(PACKAGE_VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}")
+set(PACKAGE_NAME ${TARNAME})
+set(PACKAGE_TARNAME ${TARNAME})
+set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
+set(PACKAGE_BUGREPORT "http://ufo.kit.edu/ufo/newticket")
+
+enable_testing()
+
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/common/cmake")
+
+include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
+include_directories("${CMAKE_CURRENT_SOURCE_DIR}/ufo")
+include_directories("${CMAKE_CURRENT_BINARY_DIR}/ufo")
+
+
+# --- Options -----------------------------------------------------------------
+option(WITH_PROFILING "Enable profiling" OFF)
+if (WITH_PROFILING)
+    add_definitions("-pg")
+    set(CMAKE_C_FLAGS "-pg")
+endif ()
+
+
+# --- Find packages and libraries ---------------------------------------------
+
+# These packages are required in all sub-directories because Glib and GObject is
+# part of the API/ABI.
+
+find_package(OpenCL REQUIRED)
+find_package(PkgConfig REQUIRED)
+
+pkg_check_modules(GLIB2 glib-2.0>=2.22 REQUIRED)
+pkg_check_modules(GOBJECT2 gobject-2.0>=2.22 REQUIRED)
+pkg_check_modules(GMODULE2 gmodule-2.0>=2.22 REQUIRED)
+pkg_check_modules(GTHREAD2 gthread-2.0>=2.22 REQUIRED)
+pkg_check_modules(JSON_GLIB json-glib-1.0 REQUIRED)
+pkg_check_modules(ZMQ libzmq>=3.2 REQUIRED)
+
+set(UFOCORE_DEPS
+    ${OPENCL_LIBRARIES}
+    ${GLIB2_LIBRARIES}
+    ${GOBJECT2_LIBRARIES}
+    ${GMODULE2_LIBRARIES}
+    ${GTHREAD2_LIBRARIES}
+    ${JSON_GLIB_LIBRARIES}
+    ${ZMQ_LIBRARIES})
+
+
+include_directories(
+    ${CMAKE_CURRENT_BINARY_DIR}
+    ${CMAKE_CURRENT_BINARY_DIR}/src
+    ${GLIB2_INCLUDE_DIRS}
+    ${OPENCL_INCLUDE_DIRS}
+    ${JSON_GLIB_INCLUDE_DIRS}
+    ${ZMQ_INCLUDE_DIRS})
+
+link_directories(
+    ${ZMQ_LIBRARY_DIRS}
+    ${JSON_GLIB_LIBRARY_DIRS})
+
+add_definitions("-std=c99 -pedantic -Wall -Wextra -fPIC")
+
+if (CMAKE_COMPILER_IS_GNUCC)
+    add_definitions("-Wmissing-prototypes -Wmissing-declarations -Wshadow
+    -Wpointer-arith -Wcast-align -Wwrite-strings -Wredundant-decls -Wcast-qual
+    -Wnested-externs -Winline -Wno-long-long -Wconversion -Wstrict-prototypes")
+
+    add_definitions("-Wno-unused-parameter -Wno-missing-field-initializers")
+endif()
+
+add_definitions(-DG_LOG_DOMAIN=\"Ufo\")
+
+add_subdirectory(ufo)
+add_subdirectory(docs)
+#add_subdirectory(python)
+add_subdirectory(tests)
+add_subdirectory(tools)
+
+
+# --- Package generation ------------------------------------------------------
+set(CPACK_PACKAGE_DESCRIPTION ${UFO_DESCRIPTION})
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${UFO_DESCRIPTION_SUMMARY})
+set(CPACK_PACKAGE_NAME ${TARNAME})
+
+set(CPACK_PACKAGE_CONTACT "matthias.vogelgesang at kit.edu")
+set(CPACK_PACKAGE_VENDOR "Karlsruhe Institute of Technology/IPE")
+set(CPACK_PACKAGE_VERSION_MAJOR ${PACKAGE_VERSION_MAJOR})
+set(CPACK_PACKAGE_VERSION_MINOR ${PACKAGE_VERSION_MINOR})
+set(CPACK_PACKAGE_VERSION_PATCH ${PACKAGE_VERSION_PATCH})
+set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}")
+set(VERSION ${PACKAGE_VERSION})
+
+set(CPACK_GENERATOR "DEB;RPM;")
+set(CPACK_SOURCE_GENERATOR "TGZ")
+set(CPACK_SOURCE_IGNORE_FILES "tags" ".bzr" ".swp" "~1~" ".git" ".xml")
+set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE_NAME}-${PACKAGE_VERSION}" CACHE INTERNAL "tarball basename")
+
+# --- Distro specific
+set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.6), libgcc1 (>= 1:4.1)")
+
+include(CPack)
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/common/cmake/FindFFTW3.cmake b/common/cmake/FindFFTW3.cmake
new file mode 100644
index 0000000..252fcd9
--- /dev/null
+++ b/common/cmake/FindFFTW3.cmake
@@ -0,0 +1,133 @@
+# 
+# Try to find FFTW3  library  
+# (see www.fftw.org)
+# Once run this will define: 
+# 
+# FFTW3_FOUND
+# FFTW3_INCLUDE_DIR 
+# FFTW3_LIBRARIES
+# FFTW3_LINK_DIRECTORIES
+#
+# You may set one of these options before including this file:
+#  FFTW3_USE_SSE2
+#
+#  TODO: _F_ versions.
+#
+# Jan Woetzel 05/2004
+# www.mip.informatik.uni-kiel.de
+# --------------------------------
+
+ FIND_PATH(FFTW3_INCLUDE_DIR fftw3.h
+   ${FFTW3_DIR}/include
+   ${FFTW3_HOME}/include
+   ${FFTW3_DIR}
+   ${FFTW3_HOME}
+   $ENV{FFTW3_DIR}/include
+   $ENV{FFTW3_HOME}/include
+   $ENV{FFTW3_DIR}
+   $ENV{FFTW3_HOME}
+   /usr/include
+   /usr/local/include
+   $ENV{SOURCE_DIR}/fftw3
+   $ENV{SOURCE_DIR}/fftw3/include
+   $ENV{SOURCE_DIR}/fftw
+   $ENV{SOURCE_DIR}/fftw/include
+ )
+#MESSAGE("DBG FFTW3_INCLUDE_DIR=${FFTW3_INCLUDE_DIR}")  
+
+
+SET(FFTW3_POSSIBLE_LIBRARY_PATH
+  ${FFTW3_DIR}/lib
+  ${FFTW3_HOME}/lib
+  ${FFTW3_DIR}
+  ${FFTW3_HOME}  
+  $ENV{FFTW3_DIR}/lib
+  $ENV{FFTW3_HOME}/lib
+  $ENV{FFTW3_DIR}
+  $ENV{FFTW3_HOME}  
+  /usr/lib
+  /usr/local/lib
+  $ENV{SOURCE_DIR}/fftw3
+  $ENV{SOURCE_DIR}/fftw3/lib
+  $ENV{SOURCE_DIR}/fftw
+  $ENV{SOURCE_DIR}/fftw/lib
+)
+
+  
+# the lib prefix is containe din filename onf W32, unfortuantely. JW
+# teh "general" lib: 
+FIND_LIBRARY(FFTW3_FFTW_LIBRARY
+  NAMES fftw3 libfftw libfftw3 libfftw3-3
+  PATHS 
+  ${FFTW3_POSSIBLE_LIBRARY_PATH}
+  )
+#MESSAGE("DBG FFTW3_FFTW_LIBRARY=${FFTW3_FFTW_LIBRARY}")
+
+FIND_LIBRARY(FFTW3_FFTWF_LIBRARY
+  NAMES fftwf3 fftw3f fftwf libfftwf libfftwf3 libfftw3f libfftw3f-3
+  PATHS 
+  ${FFTW3_POSSIBLE_LIBRARY_PATH}
+  )
+#MESSAGE("DBG FFTW3_FFTWF_LIBRARY=${FFTW3_FFTWF_LIBRARY}")
+
+FIND_LIBRARY(FFTW3_FFTWL_LIBRARY
+  NAMES fftwl3 fftw3l fftwl libfftwl libfftwl3 libfftw3l libfftw3l-3
+  PATHS 
+  ${FFTW3_POSSIBLE_LIBRARY_PATH}
+  )
+#MESSAGE("DBG FFTW3_FFTWF_LIBRARY=${FFTW3_FFTWL_LIBRARY}")
+
+
+FIND_LIBRARY(FFTW3_FFTW_SSE2_LIBRARY
+  NAMES fftw_sse2 fftw3_sse2 libfftw_sse2 libfftw3_sse2
+  PATHS 
+  ${FFTW3_POSSIBLE_LIBRARY_PATH}
+  )
+#MESSAGE("DBG FFTW3_FFTW_SSE2_LIBRARY=${FFTW3_FFTW_SSE2_LIBRARY}")
+
+FIND_LIBRARY(FFTW3_FFTWF_SSE_LIBRARY
+  NAMES fftwf_sse fftwf3_sse fftw3f_sse libfftwf_sse libfftwf3_sse libfftw3f_sse
+  PATHS 
+  ${FFTW3_POSSIBLE_LIBRARY_PATH}
+  )
+#MESSAGE("DBG FFTW3_FFTWF_SSE_LIBRARY=${FFTW3_FFTWF_SSE_LIBRARY}")
+
+
+# --------------------------------
+# select one of the above
+# default: 
+IF (FFTW3_FFTW_LIBRARY)
+  SET(FFTW3_LIBRARIES ${FFTW3_FFTW_LIBRARY})
+ENDIF (FFTW3_FFTW_LIBRARY)
+# specialized: 
+IF (FFTW3_USE_SSE2 AND FFTW3_FFTW_SSE2_LIBRARY)
+  SET(FFTW3_LIBRARIES ${FFTW3_FFTW_SSE2_LIBRARY})
+ENDIF (FFTW3_USE_SSE2 AND FFTW3_FFTW_SSE2_LIBRARY)
+
+# --------------------------------
+
+IF(FFTW3_LIBRARIES)
+  IF (FFTW3_INCLUDE_DIR)
+
+    # OK, found all we need
+    SET(FFTW3_FOUND TRUE)
+    GET_FILENAME_COMPONENT(FFTW3_LINK_DIRECTORIES ${FFTW3_LIBRARIES} PATH)
+    
+  ELSE (FFTW3_INCLUDE_DIR)
+    MESSAGE("FFTW3 include dir not found. Set FFTW3_DIR to find it.")
+  ENDIF(FFTW3_INCLUDE_DIR)
+ELSE(FFTW3_LIBRARIES)
+  MESSAGE("FFTW3 lib not found. Set FFTW3_DIR to find it.")
+ENDIF(FFTW3_LIBRARIES)
+
+
+MARK_AS_ADVANCED(
+  FFTW3_INCLUDE_DIR
+  FFTW3_LIBRARIES
+  FFTW3_FFTW_LIBRARY
+  FFTW3_FFTW_SSE2_LIBRARY
+  FFTW3_FFTWF_LIBRARY
+  FFTW3_FFTWF_SSE_LIBRARY
+  FFTW3_FFTWL_LIBRARY
+  FFTW3_LINK_DIRECTORIES
+)
diff --git a/common/cmake/FindGObjectIntrospection.cmake b/common/cmake/FindGObjectIntrospection.cmake
new file mode 100644
index 0000000..2073c3c
--- /dev/null
+++ b/common/cmake/FindGObjectIntrospection.cmake
@@ -0,0 +1,61 @@
+# - try to find gobject-introspection
+#
+# Once done this will define
+#
+#  INTROSPECTION_FOUND - system has gobject-introspection
+#  INTROSPECTION_SCANNER - the gobject-introspection scanner, g-ir-scanner
+#  INTROSPECTION_COMPILER - the gobject-introspection compiler, g-ir-compiler
+#  INTROSPECTION_GENERATE - the gobject-introspection generate, g-ir-generate
+#  INTROSPECTION_GIRDIR
+#  INTROSPECTION_TYPELIBDIR
+#  INTROSPECTION_CFLAGS
+#  INTROSPECTION_LIBS
+#
+# Copyright (C) 2010, Pino Toscano, <pino at kde.org>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+macro(_GIR_GET_PKGCONFIG_VAR _outvar _varname)
+  execute_process(
+    COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=${_varname} gobject-introspection-1.0
+    OUTPUT_VARIABLE _result
+    RESULT_VARIABLE _null
+  )
+
+  if (_null)
+  else()
+    string(REGEX REPLACE "[\r\n]" " " _result "${_result}")
+    string(REGEX REPLACE " +$" ""  _result "${_result}")
+    separate_arguments(_result)
+    set(${_outvar} ${_result} CACHE INTERNAL "")
+  endif()
+endmacro(_GIR_GET_PKGCONFIG_VAR)
+
+find_package(PkgConfig)
+if(PKG_CONFIG_FOUND)
+  if(PACKAGE_FIND_VERSION_COUNT GREATER 0)
+    set(_gir_version_cmp ">=${PACKAGE_FIND_VERSION}")
+  endif()
+  pkg_check_modules(_pc_gir gobject-introspection-1.0${_gir_version_cmp})
+  if(_pc_gir_FOUND)
+    set(INTROSPECTION_FOUND TRUE)
+    _gir_get_pkgconfig_var(INTROSPECTION_SCANNER "g_ir_scanner")
+    _gir_get_pkgconfig_var(INTROSPECTION_COMPILER "g_ir_compiler")
+    _gir_get_pkgconfig_var(INTROSPECTION_GENERATE "g_ir_generate")
+    _gir_get_pkgconfig_var(INTROSPECTION_GIRDIR "girdir")
+    _gir_get_pkgconfig_var(INTROSPECTION_TYPELIBDIR "typelibdir")
+    set(INTROSPECTION_CFLAGS "${_pc_gir_CFLAGS}")
+    set(INTROSPECTION_LIBS "${_pc_gir_LIBS}")
+  endif()
+endif()
+
+mark_as_advanced(
+  INTROSPECTION_SCANNER
+  INTROSPECTION_COMPILER
+  INTROSPECTION_GENERATE
+  INTROSPECTION_GIRDIR
+  INTROSPECTION_TYPELIBDIR
+  INTROSPECTION_CFLAGS
+  INTROSPECTION_LIBS
+)
diff --git a/common/cmake/FindNumpy.cmake b/common/cmake/FindNumpy.cmake
new file mode 100644
index 0000000..367c738
--- /dev/null
+++ b/common/cmake/FindNumpy.cmake
@@ -0,0 +1,57 @@
+#
+# $Id: $
+#
+# Author(s):  Anton Deguet
+# Created on: 2010-01-20
+#
+# (C) Copyright 2010 Johns Hopkins University (JHU), All Rights
+# Reserved.
+#
+# --- begin cisst license - do not edit ---
+# 
+# This software is provided "as is" under an open source license, with
+# no warranty.  The complete license can be found in license.txt and
+# http://www.cisst.org/cisst/license.txt.
+# 
+# --- end cisst license ---
+#
+# File based on FindNUMARRAY distributed with ITK 3.4 (see itk.org)
+#
+# Main modifications:
+# - use Numpy instead of Numarray for all naming
+# - added path for Python 2.5 and 2.6
+# - renamed python script generated (det_npp became determineNumpyPath)
+# - use lower case for CMake commands and keywords
+# - updated python script to use get_include, not get_numpy_include which is now deprecated
+#
+# ---
+#
+# Try to find numpy python package
+# Once done this will define
+#
+# PYTHON_NUMPY_FOUND        - system has numpy development package and it should be used
+# PYTHON_NUMPY_INCLUDE_DIR  - directory where the arrayobject.h header file can be found
+#
+#
+if(PYTHON_EXECUTABLE)
+    file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/determineNumpyPath.py "try: import numpy; print numpy.get_include()\nexcept: pass\n")
+    exec_program("${PYTHON_EXECUTABLE}"
+                 ARGS "\"${CMAKE_CURRENT_BINARY_DIR}/determineNumpyPath.py\""
+                 OUTPUT_VARIABLE NUMPY_PATH
+                 )
+endif(PYTHON_EXECUTABLE)
+
+find_path(PYTHON_NUMPY_INCLUDE_DIR arrayobject.h
+          "${NUMPY_PATH}/numpy/"
+          "${PYTHON_INCLUDE_PATH}/numpy/"
+          /usr/include/python2.6/numpy/
+          /usr/include/python2.5/numpy/
+          /usr/include/python2.4/numpy/
+          /usr/include/python2.3/numpy/
+          DOC "Directory where the arrayobject.h header file can be found. This file is part of the numpy package"
+    )
+
+if(PYTHON_NUMPY_INCLUDE_DIR)
+    set(PYTHON_NUMPY_FOUND 1 CACHE INTERNAL "Python numpy development package is available")
+endif(PYTHON_NUMPY_INCLUDE_DIR)
+
diff --git a/common/cmake/FindOCLFFT.cmake b/common/cmake/FindOCLFFT.cmake
new file mode 100644
index 0000000..fd9c156
--- /dev/null
+++ b/common/cmake/FindOCLFFT.cmake
@@ -0,0 +1,14 @@
+# Try to find liboclfft and clFFT.h. Once found the following variables will be
+# defined:
+#
+# OCLFFT_FOUND
+# OCLFFT_INCLUDE_DIRS
+# OCLFFT_LIBRARIES
+
+find_path(OCLFFT_INCLUDE_DIRS clFFT.h)
+find_library(OCLFFT_LIBRARIES oclfft)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(OCLFFT DEFAULT_MSG OCLFFT_INCLUDE_DIRS OCLFFT_LIBRARIES)
+
+mark_as_advanced(OCLFFT_INCLUDE_DIRS OCLFFT_LIBRARIES)
diff --git a/common/cmake/FindOpenCL.cmake b/common/cmake/FindOpenCL.cmake
new file mode 100644
index 0000000..f1b1301
--- /dev/null
+++ b/common/cmake/FindOpenCL.cmake
@@ -0,0 +1,89 @@
+# - Try to find OpenCL
+# This module tries to find an OpenCL implementation on your system. It supports
+# AMD / ATI, Apple and NVIDIA implementations, but shoudl work, too.
+#
+# Once done this will define
+#  OPENCL_FOUND        - system has OpenCL
+#  OPENCL_INCLUDE_DIRS  - the OpenCL include directory
+#  OPENCL_LIBRARIES    - link these to use OpenCL
+#
+# WIN32 should work, but is untested
+
+FIND_PACKAGE( PackageHandleStandardArgs )
+
+SET (OPENCL_VERSION_STRING "0.1.0")
+SET (OPENCL_VERSION_MAJOR 0)
+SET (OPENCL_VERSION_MINOR 1)
+SET (OPENCL_VERSION_PATCH 0)
+
+IF (APPLE)
+
+  FIND_LIBRARY(OPENCL_LIBRARIES OpenCL DOC "OpenCL lib for OSX")
+  FIND_PATH(OPENCL_INCLUDE_DIRS OpenCL/cl.h DOC "Include for OpenCL on OSX")
+  FIND_PATH(_OPENCL_CPP_INCLUDE_DIRS OpenCL/cl.hpp DOC "Include for OpenCL CPP bindings on OSX")
+
+ELSE (APPLE)
+
+	IF (WIN32)
+	
+	    FIND_PATH(OPENCL_INCLUDE_DIRS CL/cl.h)
+	    FIND_PATH(_OPENCL_CPP_INCLUDE_DIRS CL/cl.hpp)
+	
+	    # The AMD SDK currently installs both x86 and x86_64 libraries
+	    # This is only a hack to find out architecture
+	    IF( ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64" )
+	    	SET(OPENCL_LIB_DIR "$ENV{ATISTREAMSDKROOT}/lib/x86_64")
+	    ELSE (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64")
+	    	SET(OPENCL_LIB_DIR "$ENV{ATISTREAMSDKROOT}/lib/x86")
+	    ENDIF( ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64" )
+	    FIND_LIBRARY(OPENCL_LIBRARIES OpenCL.lib ${OPENCL_LIB_DIR})
+	    
+	    GET_FILENAME_COMPONENT(_OPENCL_INC_CAND ${OPENCL_LIB_DIR}/../../include ABSOLUTE)
+	    
+	    # On Win32 search relative to the library
+	    FIND_PATH(OPENCL_INCLUDE_DIRS CL/cl.h PATHS "${_OPENCL_INC_CAND}")
+	    FIND_PATH(_OPENCL_CPP_INCLUDE_DIRS CL/cl.hpp PATHS "${_OPENCL_INC_CAND}")
+	
+	ELSE (WIN32)
+
+            # Unix style platforms
+            FIND_LIBRARY(OPENCL_LIBRARIES OpenCL
+              ENV LD_LIBRARY_PATH
+              /usr/lib/nvidia-current
+              /usr/lib64/nvidia
+              /opt/nvidia-current
+              /opt/AMDAPP/lib
+            )
+
+            GET_FILENAME_COMPONENT(OPENCL_LIB_DIR ${OPENCL_LIBRARIES} PATH)
+            GET_FILENAME_COMPONENT(_OPENCL_INC_CAND ${OPENCL_LIB_DIR}/../../include ABSOLUTE)
+
+            # The AMD SDK currently does not place its headers
+            # in /usr/include, therefore also search relative
+            # to the library
+            FIND_PATH(OPENCL_INCLUDE_DIRS CL/cl.h PATHS 
+                ${_OPENCL_INC_CAND} 
+                /usr/local/cuda/include 
+                /opt/cuda/include
+                /opt/AMDAPP/include)
+            FIND_PATH(_OPENCL_CPP_INCLUDE_DIRS CL/cl.hpp PATHS ${_OPENCL_INC_CAND})
+
+	ENDIF (WIN32)
+
+ENDIF (APPLE)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS( OpenCL DEFAULT_MSG OPENCL_LIBRARIES OPENCL_INCLUDE_DIRS )
+
+IF( _OPENCL_CPP_INCLUDE_DIRS )
+	SET( OPENCL_HAS_CPP_BINDINGS TRUE )
+	LIST( APPEND OPENCL_INCLUDE_DIRS ${_OPENCL_CPP_INCLUDE_DIRS} )
+	# This is often the same, so clean up
+	LIST( REMOVE_DUPLICATES OPENCL_INCLUDE_DIRS )
+ENDIF( _OPENCL_CPP_INCLUDE_DIRS )
+
+MARK_AS_ADVANCED(
+  OPENCL_LIBRARIES
+  OPENCL_INCLUDE_DIRS
+  _OPENCL_CPP_INCLUDE_DIRS
+)
+
diff --git a/common/cmake/FindVala.cmake b/common/cmake/FindVala.cmake
new file mode 100644
index 0000000..2d1ed14
--- /dev/null
+++ b/common/cmake/FindVala.cmake
@@ -0,0 +1,65 @@
+##
+# Copyright 2009 Jakob Westhoff. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#    1. Redistributions of source code must retain the above copyright notice,
+#       this list of conditions and the following disclaimer.
+#
+#    2. Redistributions in binary form must reproduce the above copyright notice,
+#       this list of conditions and the following disclaimer in the documentation
+#       and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and documentation are those
+# of the authors and should not be interpreted as representing official policies,
+# either expressed or implied, of Jakob Westhoff
+##
+
+##
+# Find module for the Vala compiler (valac)
+#
+# This module determines wheter a Vala compiler is installed on the current
+# system and where its executable is.
+#
+# Call the module using "find_package(Vala) from within your CMakeLists.txt.
+#
+# The following variables will be set after an invocation:
+#
+#  VALA_FOUND       Whether the vala compiler has been found or not
+#  VALA_EXECUTABLE  Full path to the valac executable if it has been found
+#  VALA_VERSION     Version number of the available valac
+##
+
+
+# Search for the valac executable in the usual system paths.
+find_program(VALA_EXECUTABLE
+  NAMES valac)
+
+# Handle the QUIETLY and REQUIRED arguments, which may be given to the find call.
+# Furthermore set VALA_FOUND to TRUE if Vala has been found (aka.
+# VALA_EXECUTABLE is set)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Vala DEFAULT_MSG VALA_EXECUTABLE)
+
+mark_as_advanced(VALA_EXECUTABLE)
+
+# Determine the valac version
+if(VALA_FOUND)
+    execute_process(COMMAND ${VALA_EXECUTABLE} "--version"
+                    OUTPUT_VARIABLE "VALA_VERSION")
+    string(REPLACE "Vala" "" "VALA_VERSION" ${VALA_VERSION})
+    string(STRIP ${VALA_VERSION} "VALA_VERSION")
+endif(VALA_FOUND)
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644
index 0000000..0e9eecc
--- /dev/null
+++ b/docs/CMakeLists.txt
@@ -0,0 +1,41 @@
+cmake_minimum_required(VERSION 2.8)
+
+find_program(SPHINX sphinx-build PATHS /usr/local/bin /usr/bin)
+mark_as_advanced(SPHINX)
+
+# --- End-user manual ---------------------------------------------------------
+if(SPHINX)
+    option(WITH_MANUAL "Build user manual" ON)
+
+    if (WITH_MANUAL)
+        set(input_dir ${CMAKE_CURRENT_SOURCE_DIR}/manual)
+        set(output_dir ${CMAKE_CURRENT_BINARY_DIR}/manual)
+
+        file(GLOB_RECURSE sphinx_source ${input_dir}/*.rst)
+        list(APPEND sphinx_source "${output_dir}/conf.py")
+
+        set(sphinx_static
+            _static/ufo-logo.png
+            _templates/indexcontent.html
+            _templates/indexsidebar.html
+            _templates/layout.html
+            )
+
+        configure_file(${input_dir}/conf.py.in ${output_dir}/conf.py)
+        
+        foreach(file ${sphinx_static})
+            configure_file(${input_dir}/${file} ${output_dir}/${file} COPYONLY)
+        endforeach()
+
+        add_custom_command(OUTPUT ${output_dir}/html/index.html
+            COMMAND ${SPHINX} -b html -c ${output_dir} ${input_dir} html
+            DEPENDS ${sphinx_source}
+            WORKING_DIRECTORY ${output_dir}
+            COMMENT "Build Sphinx HTML")
+
+        add_custom_target(manual ALL DEPENDS ${output_dir}/html/index.html)
+
+        add_dependencies(manual ufo)
+    endif()
+endif()
+
diff --git a/docs/Ufo-docs.xml.in b/docs/Ufo-docs.xml.in
new file mode 100644
index 0000000..e4bf038
--- /dev/null
+++ b/docs/Ufo-docs.xml.in
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+]>
+<book id="index">
+  <bookinfo>
+    <title>UFO Reference Manual</title>
+    <releaseinfo>
+        for UFO ${UFO_API_VERSION}
+      The latest version of this documentation can be found on-line at
+      <ulink role="online-location"
+          url="http://ufo.kit.edu/ufo">http://ufo.kit.edu/ufo/</ulink>.
+    </releaseinfo>
+  </bookinfo>
+
+  <chapter>
+    <title>UFO API Reference</title>
+        ${_xml_doc_input}
+
+  </chapter>
+  <index id="api-index-full">
+    <title>API Index</title>
+    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+  </index>
+
+  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
diff --git a/docs/Ufo-sections.txt.in b/docs/Ufo-sections.txt.in
new file mode 100644
index 0000000..b518754
--- /dev/null
+++ b/docs/Ufo-sections.txt.in
@@ -0,0 +1,180 @@
+<SECTION>
+<FILE>ufo-config</FILE>
+<TITLE>UfoConfig</TITLE>
+UfoConfig
+ufo_config_new
+ufo_config_get_paths
+<SUBSECTION Standard>
+UFO_CONFIG
+UFO_CONFIG_CLASS
+UFO_CONFIG_GET_CLASS
+UFO_IS_CONFIG
+UFO_IS_CONFIG_CLASS
+UFO_TYPE_CONFIG
+ufo_config_get_type
+<SUBSECTION Private>
+UfoConfigPrivate
+</SECTION>
+
+<SECTION>
+<FILE>ufo-configurable</FILE>
+<TITLE>UfoConfigurable</TITLE>
+UfoConfigurable
+<SUBSECTION Standard>
+UFO_CONFIGURABLE
+UFO_CONFIGURABLE_CLASS
+UFO_CONFIGURABLE_GET_IFACE
+UFO_IS_CONFIGURABLE
+UFO_IS_CONFIGURABLE_CLASS
+UFO_TYPE_CONFIGURABLE
+ufo_configurable_get_type
+<SUBSECTION Private>
+UfoConfigurableIface
+</SECTION>
+
+<SECTION>
+<FILE>ufo-plugin-manager</FILE>
+<TITLE>UfoPluginManager</TITLE>
+UfoPluginManagerError
+UfoPluginManager
+ufo_plugin_manager_new
+ufo_plugin_manager_get_task
+ufo_plugin_manager_get_all_task_names
+<SUBSECTION Standard>
+UFO_PLUGIN_MANAGER
+UFO_IS_PLUGIN_MANAGER
+UFO_TYPE_PLUGIN_MANAGER
+ufo_plugin_manager_get_type
+UFO_PLUGIN_MANAGER_CLASS
+UFO_IS_PLUGIN_MANAGER_CLASS
+UFO_PLUGIN_MANAGER_GET_CLASS
+<SUBSECTION Private>
+UFO_PLUGIN_MANAGER_ERROR
+UfoPluginManagerPrivate
+ufo_plugin_manager_error_quark
+</SECTION>
+
+<SECTION>
+<FILE>ufo-resource-manager</FILE>
+<TITLE>UfoResources</TITLE>
+UfoResourcesError
+UfoResources
+ufo_resources_new
+ufo_resources_get_kernel
+ufo_resources_get_kernel_from_source
+ufo_resources_get_context
+<SUBSECTION Standard>
+UFO_RESOURCES
+UFO_IS_RESOURCES
+UFO_TYPE_RESOURCES
+ufo_resources_get_type
+UFO_RESOURCES_CLASS
+UFO_IS_RESOURCES_CLASS
+UFO_RESOURCES_GET_CLASS
+<SUBSECTION Private>
+UFO_RESOURCES_ERROR
+UfoResourcesClass
+UfoResourcesPrivate
+opencl_map_error
+ufo_resources_error_quark
+</SECTION>
+
+<SECTION>
+<FILE>ufo-profiler</FILE>
+<TITLE>UfoProfiler</TITLE>
+UfoProfilerFunc
+UfoProfilerLevel
+UfoProfilerTimer
+UfoProfiler
+UfoProfilerClass
+ufo_profiler_new
+ufo_profiler_call
+ufo_profiler_foreach
+ufo_profiler_start
+ufo_profiler_stop
+ufo_profiler_elapsed
+<SUBSECTION Standard>
+UFO_TYPE_PROFILER
+UFO_IS_PROFILER
+UFO_IS_PROFILER_CLASS
+UFO_PROFILER
+UFO_PROFILER_CLASS
+UFO_PROFILER_GET_CLASS
+ufo_profiler_get_type
+<SUBSECTION Private>
+UfoProfilerPrivate
+</SECTION>
+
+<SECTION>
+<FILE>ufo-graph</FILE>
+<TITLE>UfoGraph</TITLE>
+UfoGraph
+ufo_graph_new
+<SUBSECTION Standard>
+UFO_GRAPH
+UFO_IS_GRAPH
+UFO_TYPE_GRAPH
+ufo_graph_get_type
+UFO_GRAPH_CLASS
+UFO_IS_GRAPH_CLASS
+UFO_GRAPH_GET_CLASS
+<SUBSECTION Private>
+UFO_GRAPH_ERROR
+UfoGraphClass
+UfoGraphPrivate
+ufo_graph_error_quark
+</SECTION>
+
+<SECTION>
+<FILE>ufo-buffer</FILE>
+<TITLE>UfoBuffer</TITLE>
+UfoBufferError
+UfoBuffer
+UFO_BUFFER_MAX_NDIMS
+ufo_buffer_new
+ufo_buffer_copy
+ufo_buffer_get_size
+ufo_buffer_get_2d_dimensions
+ufo_buffer_resize
+ufo_buffer_get_host_array
+ufo_buffer_get_device_array
+<SUBSECTION>UfoBufferParamSpec</SUBSECTION>
+UfoBufferParamSpec
+ufo_buffer_param_spec
+<SUBSECTION Standard>
+UFO_BUFFER
+UFO_IS_BUFFER
+UFO_TYPE_BUFFER
+ufo_buffer_get_type
+UFO_BUFFER_CLASS
+UFO_IS_BUFFER_CLASS
+UFO_BUFFER_GET_CLASS
+UFO_BUFFER_PARAM_SPEC
+UFO_IS_PARAM_SPEC_BUFFER
+UFO_TYPE_PARAM_BUFFER
+ufo_buffer_param_get_type
+<SUBSECTION Private>
+UFO_BUFFER_ERROR
+UfoBufferPrivate
+UfoBufferClass
+ufo_buffer_error_quark
+</SECTION>
+
+<SECTION>
+<FILE>ufo-scheduler</FILE>
+<TITLE>UfoScheduler</TITLE>
+UfoScheduler
+ufo_scheduler_new
+ufo_scheduler_run
+<SUBSECTION Standard>
+UFO_SCHEDULER
+UFO_SCHEDULER_CLASS
+UFO_SCHEDULER_GET_CLASS
+UFO_IS_SCHEDULER
+UFO_IS_SCHEDULER_CLASS
+UFO_TYPE_SCHEDULER
+ufo_scheduler_get_type
+<SUBSECTION Private>
+UfoSchedulerClass
+UfoSchedulerPrivate
+</SECTION>
diff --git a/docs/manual/_static/ufo-logo.png b/docs/manual/_static/ufo-logo.png
new file mode 100644
index 0000000..351b64f
Binary files /dev/null and b/docs/manual/_static/ufo-logo.png differ
diff --git a/docs/manual/_templates/indexcontent.html b/docs/manual/_templates/indexcontent.html
new file mode 100644
index 0000000..33883d4
--- /dev/null
+++ b/docs/manual/_templates/indexcontent.html
@@ -0,0 +1,48 @@
+{% extends "defindex.html" %}
+{% block tables %}
+  <p><strong>Parts of the documentation:</strong></p>
+  <table class="contentstable" align="center"><tr>
+    <td width="50%">
+      <p class="biglink"><a class="biglink" href="{{ pathto("whatsnew/" + version) }}">What's new in UFO {{ version }}?</a><br/>
+          <span class="linkdescr">or <a href="{{ pathto("whatsnew/index") }}">all "What's new" documents</a></span></p>
+      <p class="biglink"><a class="biglink" href="{{ pathto("install/index") }}">Installation</a><br/>
+         <span class="linkdescr">install from either packages or source</span></p>
+      <p class="biglink"><a class="biglink" href="{{ pathto("using/index") }}">Using UFO</a><br/>
+         <span class="linkdescr">how to use UFO on different platforms</span></p>
+    </td><td width="50%">
+      <p class="biglink"><a class="biglink" href="{{ pathto("json") }}">JSON Configuration</a><br/>
+         <span class="linkdescr">configure UFO graphs textual</span></p>
+      <p class="biglink"><a class="biglink" href="{{ pathto("api/index") }}">UFO API</a><br/>
+         <span class="linkdescr">reference for C/C++ programmers</span></p>
+      <p class="biglink"><a class="biglink" href="{{ pathto("faq") }}">FAQ</a><br/>
+         <span class="linkdescr">frequently asked questions</span></p>
+    </td></tr>
+  </table>
+
+  <p><strong>Indices and tables:</strong></p>
+  <table class="contentstable" align="center"><tr>
+    <td width="50%">
+      <p class="biglink"><a class="biglink" href="{{ pathto("genindex") }}">General Index</a><br/>
+         <span class="linkdescr">all functions, classes, terms</span></p>
+      <p class="biglink"><a class="biglink" href="{{ pathto("glossary") }}">Glossary</a><br/>
+         <span class="linkdescr">the most important terms explained</span></p>
+    </td><td width="50%">
+      <p class="biglink"><a class="biglink" href="{{ pathto("search") }}">Search page</a><br/>
+         <span class="linkdescr">search this documentation</span></p>
+      <p class="biglink"><a class="biglink" href="{{ pathto("contents") }}">Complete Table of Contents</a><br/>
+         <span class="linkdescr">lists all sections and subsections</span></p>
+    </td></tr>
+  </table>
+
+  <p><strong>Meta information:</strong></p>
+  <table class="contentstable" align="center"><tr>
+    <td width="50%">
+      <p class="biglink"><a class="biglink" href="{{ pathto("todo") }}">TODO</a></p>
+      <p class="biglink"><a class="biglink" href="{{ pathto("bugs") }}">Reporting bugs</a></p>
+    </td><td width="50%">
+      <p class="biglink"><a class="biglink" href="{{ pathto("copyright") }}">Copyright</a></p>
+    </td></tr>
+  </table>
+
+{% endblock %}
+
diff --git a/docs/manual/_templates/indexsidebar.html b/docs/manual/_templates/indexsidebar.html
new file mode 100644
index 0000000..f6b3914
--- /dev/null
+++ b/docs/manual/_templates/indexsidebar.html
@@ -0,0 +1,5 @@
+    <h3>Other resources</h3>
+    <ul>
+        <li><a href="http://ufo.kit.edu">Project Homepage</a></li>
+        <li><a href="http://ufo.kit.edu/ufo">Development Homepage</a></li>
+    </ul>
diff --git a/docs/manual/_templates/layout.html b/docs/manual/_templates/layout.html
new file mode 100644
index 0000000..0429147
--- /dev/null
+++ b/docs/manual/_templates/layout.html
@@ -0,0 +1,5 @@
+{% extends "!layout.html" %}
+{% block rootrellink %}
+    <li><a href="{{ pathto('index') }}">Index</a>  | </li>
+    <li><a href="{{ pathto('contents') }}">Contents</a> »</li>
+{% endblock %}
diff --git a/docs/manual/api/general.rst b/docs/manual/api/general.rst
new file mode 100644
index 0000000..3928260
--- /dev/null
+++ b/docs/manual/api/general.rst
@@ -0,0 +1,19 @@
+================
+General Overview
+================
+
+.. default-domain:: c
+
+UFOs main purpose is to build streaming setups for fast image processing. Each
+setup consists of a :type:`UfoGraph` that contains several :type:`UfoFilter`
+nodes. Each Filter is characterized by its kernel task and the number of its
+input and output :type:`UfoChannel` elements. A Filter is added to the Graph by
+creating a new plugin instance with :func:`ufo_graph_get_filter()`. A connection
+between two Filters can be established either implicitly
+(:func:`ufo_filter_connect_to()`) or explicitly
+(:func:`ufo_filter_connect_by_name()`) which is mandatory for filters with more
+than one in- or output.
+
+Because, each Filter is derived from the `GObject` base class, the properties
+are set with :func:`g_object_set()`.
+
diff --git a/docs/manual/api/index.rst b/docs/manual/api/index.rst
new file mode 100644
index 0000000..c09bb03
--- /dev/null
+++ b/docs/manual/api/index.rst
@@ -0,0 +1,13 @@
+.. _ufo-api:
+
+=====================================
+UFO Application Programming Interface
+=====================================
+
+This information will help you understand how things work together and why some
+of the design decisions were made the way they are now.
+
+.. toctree::
+    
+    general.rst
+    ufo-0.3.rst
diff --git a/docs/manual/api/ufo-0.3.rst b/docs/manual/api/ufo-0.3.rst
new file mode 100644
index 0000000..e86002c
--- /dev/null
+++ b/docs/manual/api/ufo-0.3.rst
@@ -0,0 +1,1093 @@
+=====================
+UFO 0.3 API reference
+=====================
+
+UfoArchGraph
+============
+
+.. c:type:: UfoArchGraph
+
+    Graph structure that describes the relation between hardware
+    nodes. The contents of the :c:type:`UfoArchGraph` structure are
+    private and should only be accessed via the provided API.
+
+
+.. c:function:: UfoGraph* ufo_arch_graph_new(UfoResources* resources, GList* remote_addresses)
+
+
+    :param resources: An initialized :c:type:`UfoResources` object
+    :param remote_addresses: A :c:type:`GList` containing address strings.
+
+    :returns: A new :c:type:`UfoArchGraph`.
+
+
+.. c:function:: guint ufo_arch_graph_get_num_cpus(UfoArchGraph* self)
+
+
+    :returns: Number of CPU nodes in ``graph``.
+
+
+.. c:function:: guint ufo_arch_graph_get_num_gpus(UfoArchGraph* self)
+
+
+    :returns: Number of GPU nodes in ``graph``.
+
+
+.. c:function:: guint ufo_arch_graph_get_num_remotes(UfoArchGraph* self)
+
+
+    :returns: Number of remote nodes in ``graph``.
+
+
+.. c:function:: GList* ufo_arch_graph_get_gpu_nodes(UfoArchGraph* self)
+
+    #UfoGpuNode elements in ``graph``.
+
+    :returns: A list of
+
+
+.. c:function:: GList* ufo_arch_graph_get_remote_nodes(UfoArchGraph* self)
+
+    #UfoRemoteNode elements in ``graph``.
+
+    :returns: A list of
+
+
+UfoBuffer
+=========
+
+.. c:type:: UfoBuffer
+
+    Represents n-dimensional data. The contents of the
+    :c:type:`UfoBuffer` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoBuffer* ufo_buffer_new(UfoRequisition* requisition, gpointer context)
+
+    Create a new :c:type:`UfoBuffer`.
+
+    :param requisition: size requisition
+    :param context: cl_context to use for creating the device array
+
+    :returns: A new :c:type:`UfoBuffer` with the given dimensions.
+
+
+.. c:function:: void ufo_buffer_resize(UfoBuffer* self, UfoRequisition* requisition)
+
+    Resize an existing buffer. If the new requisition has the same
+    size as before, resizing is a no-op.
+
+    :param requisition: A :c:type:`UfoRequisition` structure
+
+
+.. c:function:: gint ufo_buffer_cmp_dimensions(UfoBuffer* self, UfoRequisition* requisition)
+
+    Compare the size of ``buffer`` with a given ``requisition``.
+
+    :param requisition: #UfoRequisition
+
+    :returns: value < 0, 0 or > 0 if requisition is smaller, equal or larger.
+
+
+.. c:function:: void ufo_buffer_get_requisition(UfoBuffer* self, UfoRequisition* requisition)
+
+    Return the size of ``buffer``.
+
+    :param requisition: A location to store the requisition of ``buffer``
+
+
+.. c:function:: gsize ufo_buffer_get_size(UfoBuffer* self)
+
+    Get the number of bytes of raw data that is managed by the
+    ``buffer``.
+
+    :returns: The size of ``buffer``'s data.
+
+
+.. c:function:: void ufo_buffer_copy(UfoBuffer* self, UfoBuffer* dst)
+
+    Copy contents of ``src`` to ``dst``. The final memory location is
+    determined by the destination buffer.
+
+    :param dst: Destination :c:type:`UfoBuffer`
+
+
+.. c:function:: UfoBuffer* ufo_buffer_dup(UfoBuffer* self)
+
+    Create a new buffer with the same requisition as ``buffer``. Note,
+    that this is not a copy of ``buffer``!
+
+    :returns: A :c:type:`UfoBuffer` with the same size as ``buffer``.
+
+
+.. c:function:: gfloat* ufo_buffer_get_host_array(UfoBuffer* self, gpointer cmd_queue)
+
+    Returns a flat C-array containing the raw float data.
+
+    :param cmd_queue: A cl_command_queue object or ``NULL``.
+
+    :returns: Float array.
+
+
+.. c:function:: gpointer ufo_buffer_get_device_array(UfoBuffer* self, gpointer cmd_queue)
+
+    Return the current cl_mem object of ``buffer``. If the data is not
+    yet in device memory, it is transfered via ``cmd_queue`` to the
+    object. If ``cmd_queue`` is ``NULL``
+
+    :param cmd_queue: A cl_command_queue object or ``NULL``.
+
+    :returns: A cl_mem object associated with ``buffer``.
+
+
+.. c:function:: void ufo_buffer_discard_location(UfoBuffer* self, UfoMemLocation location)
+
+    Discard ``location`` and use "other" location without copying to
+    it first.
+
+    :param location: Location to discard
+
+
+.. c:function:: void ufo_buffer_convert(UfoBuffer* self, UfoBufferDepth depth)
+
+    Convert host data according to its ``depth`` to the internal
+    32-bit floating point representation.
+
+    :param depth: Source bit depth of host data
+
+
+UfoBufferParam
+==============
+
+.. c:type:: UfoBufferParam
+
+
+
+UfoConfig
+=========
+
+.. c:type:: UfoConfig
+
+    A :c:type:`UfoConfig` provides access to run-time specific
+    settings.
+
+
+.. c:function:: UfoConfig* ufo_config_new()
+
+    Create a config object.
+
+    :returns: A new config object.
+
+
+.. c:function:: void ufo_config_add_paths(UfoConfig* self, GList* paths)
+
+    Add ``paths`` to the list of search paths for plugins and OpenCL
+    kernel files.
+
+    :param paths: List of strings
+
+
+.. c:function:: GList* ufo_config_get_paths(UfoConfig* self)
+
+    Get an array of path strings. file system paths. Use
+    :c:func:`g_list_free()` to free it.
+
+    :returns: A list of strings containing
+
+
+UfoCpuNode
+==========
+
+.. c:type:: UfoCpuNode
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoCpuNode` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoNode* ufo_cpu_node_new(gpointer mask)
+
+
+    :param mask: None
+
+    :returns: None
+
+
+.. c:function:: gpointer ufo_cpu_node_get_affinity(UfoCpuNode* self)
+
+    Get affinity mask of ``node``.
+
+    :returns: A pointer to the cpu_set_t mask associated with
+
+
+UfoDummyTask
+============
+
+.. c:type:: UfoDummyTask
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoDummyTask` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoNode* ufo_dummy_task_new()
+
+
+    :returns: None
+
+
+UfoGpuNode
+==========
+
+.. c:type:: UfoGpuNode
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoGpuNode` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoNode* ufo_gpu_node_new(gpointer cmd_queue)
+
+
+    :param cmd_queue: None
+
+    :returns: None
+
+
+.. c:function:: gpointer ufo_gpu_node_get_cmd_queue(UfoGpuNode* self)
+
+    Get command queue associated with ``node``.
+
+    :returns: A cl_command_queue object for ``node``.
+
+
+UfoGraph
+========
+
+.. c:type:: UfoGraph
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoGraph` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoGraph* ufo_graph_new()
+
+    Create a new :c:type:`UfoGraph` object.
+
+    :returns: A :c:type:`UfoGraph`.
+
+
+.. c:function:: void ufo_graph_register_node_type(UfoGraph* self, GType type)
+
+    Registers ``type`` to be a valid node type of this graph. If a
+    type has not be an added to ``graph``, any attempt to add such a
+    node will fail.
+
+    :param type: A :c:type:`GType`
+
+
+.. c:function:: GList* ufo_graph_get_registered_node_types(UfoGraph* self)
+
+    Get all types of nodes that can be added to ``graph``. identifiers
+    that can be added to ``graph``.
+
+    :returns: A list of :c:type:`GType`
+
+
+.. c:function:: void ufo_graph_connect_nodes(UfoGraph* self, UfoNode* source, UfoNode* target, gpointer label)
+
+    Connect ``source`` with ``target`` in ``graph`` and annotate the
+    edge with
+
+    :param source: A source node
+    :param target: A target node
+    :param label: An arbitrary label
+
+
+.. c:function:: gboolean ufo_graph_is_connected(UfoGraph* self, UfoNode* from, UfoNode* to)
+
+    Check whether ``from`` is connected to ``to``.
+
+    :param from: A source node
+    :param to: A target node
+
+    :returns: %TRUE if ``from`` is connected to ``to``, otherwise %FALSE.
+
+
+.. c:function:: void ufo_graph_remove_edge(UfoGraph* self, UfoNode* source, UfoNode* target)
+
+    Remove edge between ``source`` and ``target``.
+
+    :param source: A source node
+    :param target: A target node
+
+
+.. c:function:: gpointer ufo_graph_get_edge_label(UfoGraph* self, UfoNode* source, UfoNode* target)
+
+    Retrieve edge label between ``source`` and ``target``.
+
+    :param source: Source node
+    :param target: Target node
+
+    :returns: Edge label pointer.
+
+
+.. c:function:: guint ufo_graph_get_num_nodes(UfoGraph* self)
+
+    Get number of nodes in ``graph``. The number is always divisible
+    by two, because nodes are only part of a graph if member of an
+    edge.
+
+    :returns: Number of nodes.
+
+
+.. c:function:: GList* ufo_graph_get_nodes(UfoGraph* self)
+
+    added to ``graph``.
+
+    :returns: A list of all nodes
+
+
+.. c:function:: GList* ufo_graph_get_nodes_filtered(UfoGraph* self, UfoFilterPredicate func, gpointer user_data)
+
+    Get nodes filtered by the predicate ``func``. that are marked as
+    true by the predicate function ``func``.
+
+    :param func: Predicate function to filter out nodes
+    :param user_data: Data to be passed to ``func`` on invocation
+
+    :returns: A list of all nodes
+
+
+.. c:function:: guint ufo_graph_get_num_edges(UfoGraph* self)
+
+    Get number of edges present in ``graph``.
+
+    :returns: Number of edges.
+
+
+.. c:function:: GList* ufo_graph_get_edges(UfoGraph* self)
+
+    Get all edges contained in ``graph``. error. Release the list with
+    :c:func:`g_list_free()`.
+
+    :returns: a list of :c:type:`UfoEdge` elements or ``NULL`` on
+
+
+.. c:function:: GList* ufo_graph_get_roots(UfoGraph* self)
+
+    Get all roots of ``graph``. that do not have a predessor node.
+
+    :returns: A list of all nodes
+
+
+.. c:function:: GList* ufo_graph_get_leaves(UfoGraph* self)
+
+    Get all leaves of ``graph``. that do not have a predessor node.
+
+    :returns: A list of all nodes
+
+
+.. c:function:: GList* ufo_graph_get_predecessors(UfoGraph* self, UfoNode* node)
+
+    Get the all nodes connected to ``node``. nodes of ``node``. Free
+    the list with :c:func:`g_list_free()` but not its elements.
+
+    :param node: A :c:type:`UfoNode` whose predecessors are returned.
+
+    :returns: A list with preceeding
+
+
+.. c:function:: GList* ufo_graph_get_successors(UfoGraph* self, UfoNode* node)
+
+    Get the successors of ``node``. nodes of ``node``. Free the list
+    with :c:func:`g_list_free()` but not its elements.
+
+    :param node: A :c:type:`UfoNode` whose successors are returned.
+
+    :returns: A list with succeeding
+
+
+.. c:function:: GList* ufo_graph_get_paths(UfoGraph* self, UfoFilterPredicate pred)
+
+    Compute a list of lists that contain complete paths with nodes
+    that match a predicate function. that match ``pred``.
+
+    :param pred: A predicate function
+
+    :returns: A list of lists with paths
+
+
+.. c:function:: void ufo_graph_split(UfoGraph* self, GList* path)
+
+    Duplicate nodes between head and tail of path and insert at the
+    exact the position of where path started and ended.
+
+    :param path: A path of nodes, preferably created with :c:func:`ufo_graph_get_paths()`.
+
+
+.. c:function:: void ufo_graph_dump_dot(UfoGraph* self, gchar* filename)
+
+    Stores a GraphViz dot representation of ``graph`` in ``filename``.
+
+    :param filename: A string containing a filename
+
+
+UfoGroup
+========
+
+.. c:type:: UfoGroup
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoGroup` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoGroup* ufo_group_new(GList* targets, gpointer context, UfoSendPattern pattern)
+
+    Create a new :c:type:`UfoGroup`.
+
+    :param targets: A list of :c:type:`UfoNode` targets
+    :param context: A cl_context on which the targets should operate on.
+    :param pattern: Pattern to distribute data among the ``targets``
+
+    :returns: A new :c:type:`UfoGroup`.
+
+
+.. c:function:: void ufo_group_set_num_expected(UfoGroup* self, UfoTask* target, gint n_expected)
+
+
+    :param target: None
+    :param n_expected: None
+
+
+.. c:function:: UfoBuffer* ufo_group_pop_output_buffer(UfoGroup* self, UfoRequisition* requisition)
+
+    that must be released with
+    :c:func:`ufo_group_push_output_buffer()`.
+
+    :param requisition: Size of the buffer.
+
+    :returns: A newly allocated buffer or a re-used buffer
+
+
+.. c:function:: void ufo_group_push_output_buffer(UfoGroup* self, UfoBuffer* buffer)
+
+
+    :param buffer: None
+
+
+.. c:function:: UfoBuffer* ufo_group_pop_input_buffer(UfoGroup* self, UfoTask* target)
+
+    ufo_group_push_input_buffer().
+
+    :param target: The :c:type:`UfoTask` that is a target in ``group``
+
+    :returns: A buffer that must be released with
+
+
+.. c:function:: void ufo_group_push_input_buffer(UfoGroup* self, UfoTask* target, UfoBuffer* input)
+
+
+    :param target: None
+    :param input: None
+
+
+.. c:function:: void ufo_group_finish(UfoGroup* self)
+
+
+
+UfoInputTask
+============
+
+.. c:type:: UfoInputTask
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoInputTask` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoNode* ufo_input_task_new()
+
+
+    :returns: None
+
+
+.. c:function:: void ufo_input_task_stop(UfoInputTask* self)
+
+
+
+.. c:function:: void ufo_input_task_release_input_buffer(UfoInputTask* self, UfoBuffer* buffer)
+
+
+    :param buffer: None
+
+
+.. c:function:: UfoBuffer* ufo_input_task_get_input_buffer(UfoInputTask* self)
+
+    Get the input buffer to which we write the data received from the
+    master remote node.
+
+    :returns: A :c:type:`UfoBuffer` for writing input data.
+
+
+UfoNodes
+========
+
+.. c:type:: UfoNode
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoNode` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoNode* ufo_node_new(gpointer label)
+
+
+    :param label: None
+
+    :returns: None
+
+
+.. c:function:: gpointer ufo_node_get_label(UfoNode* self)
+
+    Get arbitrary label data of ``node``.
+
+    :returns: The label of ``node``.
+
+
+.. c:function:: UfoNode* ufo_node_copy(UfoNode* self)
+
+    Get a copy of ``node``. How "deep" the copy is, depends on the
+    inherited implementation of ``node``.
+
+    :returns: Copy of ``node``.
+
+
+.. c:function:: gboolean ufo_node_equal(UfoNode* self, UfoNode* n2)
+
+
+    :param n2: None
+
+    :returns: None
+
+
+UfoOutputTask
+=============
+
+.. c:type:: UfoOutputTask
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoOutputTask` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoNode* ufo_output_task_new(guint n_dims)
+
+
+    :param n_dims: None
+
+    :returns: None
+
+
+.. c:function:: void ufo_output_task_get_output_requisition(UfoOutputTask* self, UfoRequisition* requisition)
+
+
+    :param requisition: None
+
+
+.. c:function:: UfoBuffer* ufo_output_task_get_output_buffer(UfoOutputTask* self)
+
+    Get the output buffer from which we read the data to be sent to
+    the master remote node.
+
+    :returns: A :c:type:`UfoBuffer` for reading output data.
+
+
+.. c:function:: void ufo_output_task_release_output_buffer(UfoOutputTask* self, UfoBuffer* buffer)
+
+
+    :param buffer: None
+
+
+UfoPluginManager
+================
+
+.. c:type:: UfoPluginManager
+
+    Creates :c:type:`UfoFilter` instances by loading corresponding
+    shared objects. The contents of the :c:type:`UfoPluginManager`
+    structure are private and should only be accessed via the provided
+    API.
+
+
+.. c:function:: UfoPluginManager* ufo_plugin_manager_new(UfoConfig* config)
+
+    Create a plugin manager object to instantiate filter objects. When
+    a config object is passed to the constructor, its search-path
+    property is added to the internal search paths.
+
+    :param config: A :c:type:`UfoConfig` object or ``NULL``.
+
+    :returns: A new plugin manager object.
+
+
+.. c:function:: UfoNode* ufo_plugin_manager_get_task(UfoPluginManager* self, gchar* name)
+
+    Load a :c:type:`UfoFilter` module and return an instance. The
+    shared object name must be * constructed as "libfilter at name.so".
+
+    :param name: Name of the plugin.
+
+    :returns: #UfoFilter or ``NULL`` if module cannot be found
+
+
+.. c:function:: GList* ufo_plugin_manager_get_all_task_names(UfoPluginManager* self)
+
+    Return a list with potential filter names that match shared
+    objects in all search paths.
+
+    :returns: List of strings with filter names
+
+
+UfoProfiler
+===========
+
+.. c:type:: UfoProfiler
+
+    The :c:type:`UfoProfiler` collects and records OpenCL events and
+    stores them in a convenient format on disk or prints summaries on
+    screen.
+
+
+.. c:function:: UfoProfiler* ufo_profiler_new(UfoProfilerLevel level)
+
+    Create a profiler object.
+
+    :param level: Amount of information that should be tracked by the profiler.
+
+    :returns: A new profiler object.
+
+
+.. c:function:: void ufo_profiler_call(UfoProfiler* self, gpointer command_queue, gpointer kernel, guint work_dim, gsize* global_work_size, gsize* local_work_size)
+
+    Execute the ``kernel`` using the command queue and execution
+    parameters. The event associated with the
+    :c:func:`clEnqueueNDRangeKernel()` call is recorded and may be
+    used for profiling purposes later on.
+
+    :param command_queue: A %cl_command_queue
+    :param kernel: A %cl_kernel
+    :param work_dim: Number of working dimensions.
+    :param global_work_size: Sizes of global dimensions. The array must have at least
+    :param local_work_size: Sizes of local work group dimensions. The array must have at least ``work_dim`` entries.
+
+
+.. c:function:: void ufo_profiler_foreach(UfoProfiler* self, UfoProfilerFunc func, gpointer user_data)
+
+    Iterates through the recorded events and calls ``func`` for each
+    entry.
+
+    :param func: The function to be called for an entry
+    :param user_data: User parameters
+
+
+.. c:function:: void ufo_profiler_start(UfoProfiler* self, UfoProfilerTimer timer)
+
+    Start ``timer``. The timer is not reset but accumulates the time
+    elapsed between :c:func:`ufo_profiler_start()` and
+    :c:func:`ufo_profiler_stop()` calls.
+
+    :param timer: Which timer to start
+
+
+.. c:function:: void ufo_profiler_stop(UfoProfiler* self, UfoProfilerTimer timer)
+
+    Stop ``timer``. The timer is not reset but accumulates the time
+    elapsed between :c:func:`ufo_profiler_start()` and
+    :c:func:`ufo_profiler_stop()` calls.
+
+    :param timer: Which timer to stop
+
+
+.. c:function:: gdouble ufo_profiler_elapsed(UfoProfiler* self, UfoProfilerTimer timer)
+
+    Get the elapsed time in seconds for ``timer``.
+
+    :param timer: Which timer to start
+
+    :returns: Elapsed time in seconds.
+
+
+UfoRemoteNode
+=============
+
+.. c:type:: UfoRemoteNode
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoRemoteNode` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoNode* ufo_remote_node_new(gpointer zmq_context, gchar* address)
+
+
+    :param zmq_context: None
+    :param address: None
+
+    :returns: None
+
+
+.. c:function:: guint ufo_remote_node_get_num_gpus(UfoRemoteNode* self)
+
+
+    :returns: None
+
+
+.. c:function:: void ufo_remote_node_request_setup(UfoRemoteNode* self)
+
+
+
+.. c:function:: void ufo_remote_node_send_json(UfoRemoteNode* self, gchar* json, gsize size)
+
+
+    :param json: None
+    :param size: None
+
+
+.. c:function:: void ufo_remote_node_get_structure(UfoRemoteNode* self, guint* n_inputs, UfoInputParam** in_params, UfoTaskMode* mode)
+
+
+    :param n_inputs: None
+    :param in_params: None
+    :param mode: None
+
+
+.. c:function:: void ufo_remote_node_send_inputs(UfoRemoteNode* self, UfoBuffer** inputs)
+
+
+    :param inputs: None
+
+
+.. c:function:: void ufo_remote_node_get_result(UfoRemoteNode* self, UfoBuffer* result)
+
+
+    :param result: None
+
+
+.. c:function:: void ufo_remote_node_get_requisition(UfoRemoteNode* self, UfoRequisition* requisition)
+
+
+    :param requisition: None
+
+
+.. c:function:: void ufo_remote_node_cleanup(UfoRemoteNode* self)
+
+
+
+UfoRemoteTask
+=============
+
+.. c:type:: UfoRemoteTask
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoRemoteTask` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoNode* ufo_remote_task_new()
+
+
+    :returns: None
+
+
+UfoResources
+============
+
+.. c:type:: UfoResources
+
+    Manages OpenCL resources. The contents of the
+    :c:type:`UfoResources` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoResources* ufo_resources_new(UfoConfig* config)
+
+    Create a new :c:type:`UfoResources` instance.
+
+    :param config: A :c:type:`UfoConfiguration` object or ``NULL``
+
+    :returns: A new :c:type:`UfoResources`
+
+
+.. c:function:: gpointer ufo_resources_get_kernel(UfoResources* self, gchar* filename, gchar* kernel)
+
+    Loads a and builds a kernel from a file. The file is searched in
+    the current working directory and all paths added through
+    ufo_resources_add_paths ().
+
+    :param filename: Name of the .cl kernel file
+    :param kernel: Name of a kernel
+
+    :returns: a cl_kernel object that is load from ``filename`` or ``NULL`` on error
+
+
+.. c:function:: gpointer ufo_resources_get_kernel_from_source(UfoResources* self, gchar* source, gchar* kernel)
+
+    Loads and builds a kernel from a string.
+
+    :param source: OpenCL source string
+    :param kernel: Name of a kernel
+
+    :returns: a cl_kernel object that is load from ``filename``
+
+
+.. c:function:: gpointer ufo_resources_get_context(UfoResources* self)
+
+    Returns the OpenCL context object that is used by the resource
+    resources. This context can be used to initialize othe third-party
+    libraries.
+
+    :returns: A cl_context object.
+
+
+.. c:function:: GList* ufo_resources_get_cmd_queues(UfoResources* self)
+
+    Get all command queues managed by ``resources``. cl_command_queue
+    objects. Free with :c:func:`g_list_free()` but not its elements.
+
+    :returns: List with
+
+
+UfoScheduler
+============
+
+.. c:type:: UfoScheduler
+
+    The base class scheduler is responsible of assigning command
+    queues to filters (thus managing GPU device resources) and decide
+    if to run a GPU or a CPU. The actual schedule planning can be
+    overriden.
+
+
+.. c:function:: UfoScheduler* ufo_scheduler_new(UfoConfig* config, GList* remotes)
+
+    Creates a new :c:type:`UfoScheduler`.
+
+    :param config: A :c:type:`UfoConfig` or ``NULL``
+    :param remotes: A :c:type:`GList` with strings describing remote machines or ``NULL``
+
+    :returns: A new :c:type:`UfoScheduler`
+
+
+.. c:function:: void ufo_scheduler_run(UfoScheduler* self, UfoTaskGraph* task_graph)
+
+
+    :param task_graph: None
+
+
+.. c:function:: gpointer ufo_scheduler_get_context(UfoScheduler* self)
+
+    Get the associated OpenCL context of ``scheduler``.
+
+    :returns: An cl_context structure or ``NULL`` on error.
+
+
+.. c:function:: void ufo_scheduler_set_task_split(UfoScheduler* self, gboolean split)
+
+    Sets whether the task graph should be split before execution to
+    increase multi GPU performance.
+
+    :param split: %TRUE if task graph should be split
+
+
+UfoTaskGraph
+============
+
+.. c:type:: UfoTaskGraph
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoTaskGraph` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: UfoGraph* ufo_task_graph_new()
+
+    Create a new task graph without any nodes.
+
+    :returns: A :c:type:`UfoGraph` that can be upcast to a :c:type:`UfoTaskGraph`.
+
+
+.. c:function:: void ufo_task_graph_read_from_file(UfoTaskGraph* self, UfoPluginManager* manager, gchar* filename)
+
+    Read a JSON configuration file to fill the structure of ``graph``.
+
+    :param manager: A :c:type:`UfoPluginManager` used to load the filters
+    :param filename: Path and filename to the JSON file
+
+
+.. c:function:: void ufo_task_graph_read_from_data(UfoTaskGraph* self, UfoPluginManager* manager, gchar* json)
+
+    Read a JSON configuration file to fill the structure of ``graph``.
+
+    :param manager: A :c:type:`UfoPluginManager` used to load the filters
+    :param json: ``NULL``-terminated string with JSON data
+
+
+.. c:function:: void ufo_task_graph_save_to_json(UfoTaskGraph* self, gchar* filename)
+
+    Save a JSON configuration file with the filter structure of
+    ``graph``.
+
+    :param filename: Path and filename to the JSON file
+
+
+.. c:function:: void ufo_task_graph_map(UfoTaskGraph* self, UfoArchGraph* arch_graph)
+
+    Map task nodes of ``task_graph`` to the processing nodes of
+    ``arch_graph``. Not doing this could break execution of
+    ``task_graph``.
+
+    :param arch_graph: A :c:type:`UfoArchGraph` to which ``task_graph``'s nodes are mapped onto
+
+
+.. c:function:: void ufo_task_graph_split(UfoTaskGraph* self, UfoArchGraph* arch_graph)
+
+    Splits ``task_graph`` in a way that most of the resources in
+    ``arch_graph`` can be occupied. In the simple pipeline case, the
+    longest possible GPU paths are duplicated as much as there are
+    GPUs in ``arch_graph``.
+
+    :param arch_graph: A :c:type:`UfoArchGraph`
+
+
+.. c:function:: void ufo_task_graph_connect_nodes(UfoTaskGraph* self, UfoTaskNode* n1, UfoTaskNode* n2)
+
+    Connect ``n1`` with ``n2`` using ``n2``'s default input port. To
+    specify any other port, use
+    :c:func:`ufo_task_graph_connect_nodes_full()`.
+
+    :param n1: A source node
+    :param n2: A destination node
+
+
+.. c:function:: void ufo_task_graph_connect_nodes_full(UfoTaskGraph* self, UfoTaskNode* n1, UfoTaskNode* n2, guint input)
+
+    Connect ``n1`` with ``n2`` using ``n2``'s ``input`` port.
+
+    :param n1: A source node
+    :param n2: A destination node
+    :param input: Input port of ``n2``
+
+
+.. c:function:: void ufo_task_graph_fuse(UfoTaskGraph* self)
+
+    Fuses task nodes to increase data locality.
+
+
+UfoTaskNode
+===========
+
+.. c:type:: UfoTaskNode
+
+    Main object for organizing filters. The contents of the
+    :c:type:`UfoTaskNode` structure are private and should only be
+    accessed via the provided API.
+
+
+.. c:function:: void ufo_task_node_set_plugin_name(UfoTaskNode* self, gchar* name)
+
+
+    :param name: None
+
+
+.. c:function:: gchar* ufo_task_node_get_plugin_name(UfoTaskNode* self)
+
+
+    :returns: None
+
+
+.. c:function:: gchar* ufo_task_node_get_unique_name(UfoTaskNode* self)
+
+
+    :returns: None
+
+
+.. c:function:: void ufo_task_node_set_send_pattern(UfoTaskNode* self, UfoSendPattern pattern)
+
+
+    :param pattern: None
+
+
+.. c:function:: UfoSendPattern ufo_task_node_get_send_pattern(UfoTaskNode* self)
+
+
+    :returns: None
+
+
+.. c:function:: void ufo_task_node_set_num_expected(UfoTaskNode* self, guint pos, gint n_expected)
+
+
+    :param pos: None
+    :param n_expected: None
+
+
+.. c:function:: gint ufo_task_node_get_num_expected(UfoTaskNode* self, guint pos)
+
+
+    :param pos: None
+
+    :returns: None
+
+
+.. c:function:: void ufo_task_node_set_out_group(UfoTaskNode* self, UfoGroup* group)
+
+
+    :param group: None
+
+
+.. c:function:: UfoGroup* ufo_task_node_get_out_group(UfoTaskNode* self)
+
+    Get the current out of ``node``. The out group is used to fetch
+    the ouput buffer for ``node`` using
+    :c:func:`ufo_group_pop_output_buffer()`.
+
+    :returns: The out group of ``node``.
+
+
+.. c:function:: void ufo_task_node_add_in_group(UfoTaskNode* self, guint pos, UfoGroup* group)
+
+
+    :param pos: None
+    :param group: None
+
+
+.. c:function:: UfoGroup* ufo_task_node_get_current_in_group(UfoTaskNode* self, guint pos)
+
+    Several nodes can be connected to input ``pos`` of ``node``.
+    However, at a time currently selected input group at ``pos``.
+
+    :param pos: Input position of ``node``
+
+    :returns: The current in group of ``node`` for ``pos``.
+
+
+.. c:function:: void ufo_task_node_switch_in_group(UfoTaskNode* self, guint pos)
+
+
+    :param pos: None
+
+
+.. c:function:: void ufo_task_node_set_proc_node(UfoTaskNode* self, UfoNode* proc_node)
+
+
+    :param proc_node: None
+
+
+.. c:function:: UfoNode* ufo_task_node_get_proc_node(UfoTaskNode* self)
+
+    Get the associated processing node of ``node``.
+
+    :returns: A :c:type:`UfoNode`.
diff --git a/docs/manual/bugs.rst b/docs/manual/bugs.rst
new file mode 100644
index 0000000..a0a5d82
--- /dev/null
+++ b/docs/manual/bugs.rst
@@ -0,0 +1,18 @@
+.. _reporting-bugs:
+
+==============
+Reporting Bugs
+==============
+
+Using the Trac bug tracker
+============================
+
+Bug reports regarding the UFO framework itself should be submitted via the `Trac
+bug tracker`__ by choosing the ``ufo-core`` component. Anything related to
+specific filter behaviour are classified under the ``ufo-filters`` component.
+
+The Trac system also provides lists of all tickets concerning `core`_ and `filters`_.
+
+.. __: http://ufo.kit.edu/ufo/newticket
+.. _core: http://ufo.kit.edu/ufo/report/10
+.. _filters: http://ufo.kit.edu/ufo/report/11
diff --git a/docs/manual/conf.py.in b/docs/manual/conf.py.in
new file mode 100644
index 0000000..6f40fca
--- /dev/null
+++ b/docs/manual/conf.py.in
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+#
+# UFO documentation build configuration file, created by
+# sphinx-quickstart on Mon Apr 18 12:10:11 2011.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os, time
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath' ]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+source_encoding = 'utf-8'
+
+# The master toctree document.
+#master_doc = 'index'
+
+# General information about the project.
+project = u'UFO'
+copyright = u'%s, UFO Development Team a Collaboration of KIT, SCI and TPU' % time.strftime('%Y')
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = "${UFO_VERSION_MAJOR}.${UFO_VERSION_MINOR}"
+# The full version, including alpha/beta/rc tags.
+release = "${PACKAGE_VERSION}"
+
+primary_domain = 'c'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'default'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+html_theme_options = {
+    'stickysidebar': 'true'
+}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+html_logo = "_static/ufo-logo.png"
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+html_sidebars = {
+    'index' : 'indexsidebar.html'
+}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+html_additional_pages = {
+    'index' : 'indexcontent.html'
+}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'UFOdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'UFO.tex', u'UFO Documentation',
+   u'Matthias Vogelgesang', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
diff --git a/docs/manual/contents.rst b/docs/manual/contents.rst
new file mode 100644
index 0000000..25e0e23
--- /dev/null
+++ b/docs/manual/contents.rst
@@ -0,0 +1,17 @@
+==========================
+UFO Documentation contents
+==========================
+
+.. toctree::
+
+    whatsnew/index.rst
+    install/index.rst
+    using/index.rst
+    api/index.rst
+    json.rst
+    faq.rst
+    glossary.rst
+
+    bugs.rst
+    copyright.rst
+
diff --git a/docs/manual/copyright.rst b/docs/manual/copyright.rst
new file mode 100644
index 0000000..5cdb6d3
--- /dev/null
+++ b/docs/manual/copyright.rst
@@ -0,0 +1,8 @@
+=========
+Copyright
+=========
+
+UFO and this documentation is:
+
+Copyright 2011 Karlsruhe Institute of Technology (Institute for Data Processing
+and Electronics) and Tomsk Polytechnic University
diff --git a/docs/manual/faq.rst b/docs/manual/faq.rst
new file mode 100644
index 0000000..6e738d5
--- /dev/null
+++ b/docs/manual/faq.rst
@@ -0,0 +1,153 @@
+.. _faq:
+
+==========================
+Frequently Asked Questions
+==========================
+
+Installation
+============
+
+.. _faq-linker-cant-find-libufo:
+
+Why can't the linker find libufo.so?
+----------------------------------------
+
+In the rare circumstances that you installed UFO from source for the first time
+by calling ``make install``, the dynamic linker does not know that the library
+exists. If this is the case issue ::
+
+  $ sudo ldconfig
+
+on Debian systems or ::
+
+  $ su
+  $ ldconfig
+
+on openSUSE systems.
+
+If this is not working, the library is neither installed into ``/usr/lib`` nor
+``/usr/local/lib`` on 32-bit systems or ``/usr/lib64`` and ``/usr/local/lib64``
+on 64-bit systems. Make sure not to mess with the ``CMAKE_INSTALL_PREFIX``
+during source :ref:`configuration <inst-installing-into-non-standard-directories>`.
+
+
+Usage
+=====
+
+.. _faq-filter-not-found:
+
+Why do I get a "libfilter<foo>.so not found" message?
+-------------------------------------------------------
+
+Because the UFO core system is unable to locate the filters. By default it looks
+into ``${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/ufo``, where the installation
+prefix is usually something like ``/usr`` or ``/usr/local``. If you don't want
+to install the filters system-wide, you can tell the system to try other paths
+as well::
+
+  >>> from gi.repository import Ufo
+  >>> graph = Ufo.Graph(paths='/home/user/path/to/filters:/another/path')
+
+
+Can I split a linear data stream?
+---------------------------------
+
+The output data stream of a node can be split by setting the
+``UFO_SEND_SEQUENTIAL`` mode and adjusting the number of expecting data items on
+each connected node::
+
+    from gi.repository import Ufo
+
+    out_node.set_send_pattern(Ufo.SendPattern.SEQUENTIAL)
+    in1_node.set_num_expected(0, 5)  # expect five items on the first input
+    in2_node.set_num_expected(0, -1) # expect all items
+
+    g = Ufo.TaskGraph()
+    g.connect_nodes(out_node, in1_node)
+    g.connect_nodes(out_node, in2_node)
+
+The connection order matters here! If it would be reversed, ``in2_node`` would
+receive all items whereas ``in1_node`` wouldn't receive anything.
+
+
+How can I control the debug output from libufo?
+-----------------------------------------------
+
+Generally, UFO emits debug messages under the log domain ``Ufo``. To handle
+these messages you must set a log handler_ that decides what to do with the
+messages. To ignore all messages in Python, you would have to write something
+like this::
+
+    from gi.repository import Ufo, GLib
+
+    def ignore_message(domain, level, message, user):
+        pass
+
+    if __name__ == '__main__':
+        GLib.log_set_handler("Ufo", GLib.LogLevelFlags.LEVEL_MASK,
+            ignore_message, None)
+
+.. _handler: http://developer.gnome.org/glib/unstable/glib-Message-Logging.html#g-log-set-handler
+
+
+.. _faq-numpy-output:
+
+How can I use Numpy output?
+---------------------------
+
+Install the Python extension module from ``vogelgesang/ufonp`` with ``setup.py``
+like this::
+
+    $ cd <path-to-ufonp>
+    $ python setup.py build
+    $ sudo python setup.py install
+
+You can then use the BufferInput filter to process Numpy arrays data::
+
+    from gi.repository import Ufo
+    import ufotools
+    import numpy as np
+
+    arrays = [ i*np.eye(100, dtype=np.float32) for i in range(1, 10) ]
+    buffers = [ ufotools.fromarray(a) for a in arrays ]
+
+    g = Ufo.Graph()
+    numpy_input = g.get_filter('bufferinput')
+    numpy_input.set_properties(buffers=buffers)
+
+
+How can I instantiate and pass parameters when creating a filter?
+-----------------------------------------------------------------
+
+Yes, the same module that is used to access Numpy buffers has a convenience
+wrapper around the :c:type:`UfoGraph` class that provides a ``new_filter`` method::
+
+    import ufotools.patch
+
+    g = ufotools.patch.Graph()
+    rd = g.new_filter('reader', path='/home/src', count=5)
+    wr = g.new_filter('writer', path='/home/dst', prefix='foo-')
+
+
+.. _faq-synchronize-properties:
+
+How can I synchronize two properties?
+-------------------------------------
+
+Although this is a general GObject question, synchronizing two properties is
+particularly important if the receiving filter depends on a changed property.
+For example, the back-projection should start only if a center-of-rotation is
+known. In Python you can use the ``bind_property`` function from the
+``ufotools`` module like this::
+
+    from gi.repository import Ufo
+    import ufotools.bind_property
+
+    g = Ufo.Graph()
+    cor = g.get_filter('centerofrotation')
+    bp = g.get_filter('backproject')
+
+    # Now connect the properties
+    ufotools.bind_property(cor, 'center', bp, 'axis-pos')
+
+In C, the similar ``g_object_bind_property`` function is provided out-of-the-box.
diff --git a/docs/manual/glossary.rst b/docs/manual/glossary.rst
new file mode 100644
index 0000000..efe77b7
--- /dev/null
+++ b/docs/manual/glossary.rst
@@ -0,0 +1,14 @@
+.. _glossary:
+
+========
+Glossary
+========
+
+.. keep alphetical sort order!
+
+.. glossary::
+
+    UFO
+        Ultra fast X-ray imaging of scientific processes with on-line assessment
+        and data-driven process control. Now figure out where the individual
+        abbreviation letters come from.
diff --git a/docs/manual/install/index.rst b/docs/manual/install/index.rst
new file mode 100644
index 0000000..bc4781b
--- /dev/null
+++ b/docs/manual/install/index.rst
@@ -0,0 +1,14 @@
+.. _installation:
+
+============
+Installation
+============
+
+In this section, information about how to install the UFO library and the
+accompanying filter suite is described.
+
+.. toctree::
+    :maxdepth: 2
+
+    linux.rst
+    mac.rst
diff --git a/docs/manual/install/linux.rst b/docs/manual/install/linux.rst
new file mode 100644
index 0000000..b3fa48a
--- /dev/null
+++ b/docs/manual/install/linux.rst
@@ -0,0 +1,175 @@
+.. _installation-linux:
+
+#####################
+Installation on Linux
+#####################
+
+====================
+Building from source
+====================
+
+UFO has only a few hard source dependencies, namely
+
+  - `GLib 2.0 <http://developer.gnome.org/glib/stable/>`_,
+  - `JSON-GLib 1.0 <http://live.gnome.org/JsonGlib>`_
+  - `ZeroMQ 3.2 <http://zeromq.org>`_ and
+  - a valid OpenCL installation.
+
+Furthermore, it is necessary to build the framework with a recent version of
+`CMake <http://cmake.org>`_.  `Sphinx <http://sphinx.pocoo.org>`_ is used to
+create this documentation and `Bazaar <bazaar.canonical.com>`_ for revision
+control.
+
+In case you use openSUSE, just issue ::
+
+    $ zypper install gcc gcc-c++ glib2-devel json-glib-devel
+    $ zypper install gobject-introspection-devel python-gobject2
+    $ zypper install gtk-doc python-Sphinx
+    $ zypper install libtiff-devel
+
+to install dependencies from the package repositories. Depending on which system
+you are using you have to install ZeroMQ 3.2 from source ::
+
+    $ wget http://download.zeromq.org/zeromq-3.2.2.tar.gz
+    $ tar xfz zeromq-3.2.2.tar.gz
+    $ cd zeromq-3.2.2
+    $ ./configure && make && make distcheck
+    $ make install
+
+OpenCL development files must be installed in order to build UFO. However, we
+cannot give general advices as installation procedures vary between different
+vendors. However, our CMake build facility is in most cases intelligent enough
+to find header files and libraries for NVIDIA CUDA and AMD APP SDKs.
+
+
+Retrieving the source code
+==========================
+
+In an empty directory, issue the following commands to retrieve the current
+unstable version of the source::
+
+    $ git clone http://ufo.kit.edu/git/ufo-core
+    $ git clone http://ufo.kit.edu/git/ufo-filters
+    $ git clone http://ufo.kit.edu/git/oclfft
+
+    OR
+
+    $ git clone git at ufo.kit.edu:ufo-core
+    $ git clone git at ufo.kit.edu:ufo-filters
+    $ git clone git at ufo.kit.edu:oclfft
+
+The latter is used for developers who have write-access to the corresponding
+repositories. All stable versions are tagged. To see a list of all releases
+issue::
+
+    $ git tag -l
+
+
+Quick deployment
+================
+
+UFO comes with a deploy script located in ``$UFO_ROOT/tools``. If executed
+without any arguments it will try to build and install ZeroMQ, UFO (core and
+filters) and oclfft for ``/usr/local``. You can set the installation prefix as
+an argument to install it into your home directory.
+
+
+System-wide installation
+========================
+
+If you have root access on the build machine, you can install the libraries and
+tools system-wide so that every user can access them.
+
+Building ufo-core
+-----------------
+
+Change into another empty `build` directory and issue the following commands to
+configure ::
+
+  $ cmake <path-to-ufo>
+
+CMake will notify you, if some of the dependencies are not met. In case you want
+to install the library system-wide on a 64-bit machine you should generate the
+Makefiles with ::
+
+  $ cmake <path-to-ufo> -DLIB_SUFFIX=64
+
+For earlier versions of PyGObject, it is necessary that the introspection files
+are located under ``/usr`` not ``/usr/local``. You can force the prefix by
+calling ::
+
+  $ cmake <path-to-ufo> -DCMAKE_INSTALL_PREFIX=/usr
+
+Last but not least build the framework, introspection files, API reference and
+the documentation using ::
+
+  $ make
+
+You should now run some basic tests with ::
+
+  $ make test
+
+If everything went well, you can install the library with ::
+
+  $ make install
+
+You can also build ``RPM`` and ``DEB`` packages with ::
+
+  $ make package
+
+and source tarballs with ::
+
+  $ make package_source
+
+.. seealso:: :ref:`faq-linker-cant-find-libufo`
+
+
+Building ufo-filters
+--------------------
+
+Once ufo-core is installed you can build the filter suite in a pretty similar
+way ::
+
+    $ mkdir -p build/ufo-filters
+    $ cd build/ufo-filters
+    $ cmake <path-to-ufo-filters> -DLIB_SUFFIX=64 -DCMAKE_INSTALL_PREFIX=/usr
+    $ make
+    $ make install
+
+
+.. _inst-installing-into-non-standard-directories:
+
+Installing into non-standard directories
+========================================
+
+It is possible to install the library in a non-standard directory, for example
+in the home directory of a user. In case we want to install in ``~/tmp/usr``, we
+have to configure ufo-core like this ::
+
+  $ mkdir -p build/ufo-core
+  $ cd build/ufo-core
+  $ cmake <path-to-ufo> -DCMAKE_INSTALL_PREFIX=/home/user/tmp/usr
+  $ make && make install
+
+Now, we have to adjust the ``pkg-config`` path, so that the library can be
+found when configuring the filters ::
+
+  $ export PKG_CONFIG_PATH=/home/user/tmp/usr/lib/pkgconfig
+  $ mkdir -p build/ufo-filters
+  $ cd build/ufo-filters
+  $ cmake <path-to-ufo-core> -DCMAKE_INSTALL_PREFIX=/home/user/tmp/usr
+  $ make && make install
+
+After installation you have to set the typelib and linker path so that
+everything is found at run-time ::
+
+  $ export GI_TYPELIB_PATH=/home/user/tmp/usr/lib/girepository-1.0
+  $ export LD_LIBRARY_PATH=/home/user/tmp/usr/lib:$LD_LIBRARY_PATH
+
+.. note::
+
+    It is strongly discouraged to abuse the library path for permanent
+    usage. Read some good arguments `here`__ and `here`__.
+
+__ http://web.archive.org/web/20060719201954/http://www.visi.com/~barr/ldpath.html
+__ http://linuxmafia.com/faq/Admin/ld-lib-path.html
diff --git a/docs/manual/install/mac.rst b/docs/manual/install/mac.rst
new file mode 100644
index 0000000..3398838
--- /dev/null
+++ b/docs/manual/install/mac.rst
@@ -0,0 +1,69 @@
+.. _installation-mac:
+
+Installation on MacOS X (Lion 10.7)
+===================================
+
+Preface: This information is kindly provided by Andrey Shkarin and Roman
+Shkarin.
+
+.. highlight:: bash
+
+1. Install the MacPorts from http://macports.org
+
+   .. note:: 
+   
+       If you previously installed MacPorts, and it can not be started after
+       latest installation. `Error: port dlopen (...`
+       You must download the tar.gz file and install it using a terminal::
+
+           ./configure
+           make
+           sudo make install
+
+2. Install the necessary packages through macports::
+
+       sudo port install glib2
+       sudo port install gtk2
+       sudo port install json-glib
+
+3. Install CMake from http://cmake.org
+
+4. Make ufo-core
+
+   1. Got to the directory ufo-core and run::
+
+       export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+       cmake .
+
+   2. Run::
+
+       make
+       sudo make install
+
+   3. Installation is complete, perhaps the last lines are as follows::
+
+       -- Installing: /usr/local/lib/pkgconfig/ufo.pc
+       CMake Error at src/bindings/cmake_install.cmake:33 (FILE):
+       file INSTALL cannot find
+         "/Users/Andrey/Desktop/ufo-distr/ufo-core/src/bindings/../src/Ufo-0.1.gir".
+       Call Stack (most recent call first):
+         src/cmake_install.cmake:60 (INCLUDE)
+         cmake_install.cmake:32 (INCLUDE)
+       make: *** [install] Error 1
+
+5. Make filters
+
+   1. Go to ufo-filters directory. Now, since libufo was installed in lib64, we must update the paths to look
+   for shared libraries::
+
+       export DYLD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64
+       export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+
+   2. Run::
+
+       cmake .
+       make
+       sudo make install
+
+6. Build the test project and verify that everything works.
+
diff --git a/docs/manual/json.rst b/docs/manual/json.rst
new file mode 100644
index 0000000..c2c7c1e
--- /dev/null
+++ b/docs/manual/json.rst
@@ -0,0 +1,144 @@
+.. _json-configuration:
+
+.. highlight:: javascript
+
+=========================
+JSON Configuration Format
+=========================
+
+JSON_ is a self-contained, human-readable data-interchange format. It is pure
+Unicode text and language independent. The main structures objects containing
+key/value pairs (hash-tables, dictionaries, associative arrays ...) and ordered
+lists (arrays, vectors, sequences ...) of objects or values. For a complete
+description you may refer to the complete reference at `json.org
+<http://json.org>`_.
+
+The configuration of a filter setup is stored in a JSON-encoded text file with a
+``.json`` suffix. The root object must at least contain a ``nodes`` and an
+``edges`` array ::
+
+    { "nodes": [], "edges": [] }
+
+The root object may also define several ``prop-set`` s for further reference.
+
+
+Nodes array
+===========
+
+The nodes array contains filter objects that are executed on run-time.
+Information how they are connected is provided in the `Edges array`_.
+
+Filter object
+-------------
+
+A filter consists at least of a ``plugin`` key string pointing to the filter
+that is going to be used and a ``name`` string field for unique identification.
+Of course, plugins have to be available as a shared object in UFO's path.
+
+.. highlight:: python
+
+To configure the filter, the ``properties`` field can be used. This is an object
+that maps string keys specifying the actual filter property to the value.
+Therefore, the Python code to set a property ::
+
+    reader = graph.get_filter('reader')
+    reader.set_properties(path='/home/user/data/*.tif', count=5)
+
+.. highlight:: javascript
+
+translates to ::
+
+    { "path": "/home/user/data/*.tif", "count": 5 }
+
+Instead of defining recurring properties for each filter, you can also use
+pre-defined `Property sets`_.
+
+Example nodes array
+-------------------
+ 
+An example node array looks like this::
+
+    "nodes" : [
+        {
+            "plugin": "reader",
+            "name": "reader",
+            "properties" : { "path": "/home/user/data/*.tif", "count": 5 }
+        },
+        {
+            "plugin": "writer",
+            "name": "writer"
+        }
+    ]
+
+
+Edges array
+===========
+
+The edges array specifies how the nodes in a `Nodes array`_ are connected. Each
+entry is an object that contains two objects ``from`` and ``to``. In both
+objects you have to specify at least the node name with the ``name`` key.
+Furthermore, if there are several inputs or outputs on a node, you have to tell
+which input and output to use with the ``input`` on the ``to`` node and the
+``output`` key on the ``from`` node. If you omit these, they are assumed to be
+0.
+
+To connect the nodes defined in the `Example nodes array`_ all you have to do is ::
+
+    "edges" : [
+        { 
+            "from": {"name": "reader"},
+            "to": {"name": "writer", "input": 2}
+        } 
+    ]
+
+Note, that the names specify the name of the node, not the plugin.
+
+Property sets
+=============
+
+To avoid to list the same properties for different filters over and over again,
+properties can be pre-defined with a singular top-level ``prop-sets`` object.
+Each key is a name that can be referenced in filter nodes using the
+``prop-refs`` array. The values are ordinary ``property`` mappings::
+
+    {
+        "prop-sets" : {
+            "foo-prop": {
+                "path": "/home/user/path/to/projections/*.tif", 
+                "count": 300
+            } 
+        },
+        "nodes" : [
+            {
+                "plugin": "reader",
+                "name": "reader",
+                "prop-refs": ["foo-prop"]
+            }
+        ]
+    }
+
+
+Loading and Saving the Graph
+============================
+
+.. highlight:: python
+
+The ``UfoGraph`` class exports the ``ufo_graph_read_from_json`` and
+``ufo_graph_read_save_to_json`` methods which are responsible for loading and
+saving the graph. In Python this would simply be::
+
+    from gi.repository import Ufo
+
+    g1 = Ufo.Graph()
+
+    # set up the filters using graph.get_filter() and filter.connect_to()
+
+    g1.run()
+    g1.save_to_json('graph.json')
+
+    g2 = Ufo.Graph()
+    g2.load_from_json('graph.json')
+    g2.run()
+
+
+.. _JSON: http://json.org
diff --git a/docs/manual/using/background.rst b/docs/manual/using/background.rst
new file mode 100644
index 0000000..7a26716
--- /dev/null
+++ b/docs/manual/using/background.rst
@@ -0,0 +1,86 @@
+.. _using-objects:
+
+====================
+Technical Background
+====================
+
+Relationship between graph and scheduler
+========================================
+
+A ``Ufo.Graph`` represents a network of interconnected filter nodes. New nodes
+can be added and existing node relationships be queried. Also, the graph can be
+serialized as a JSON structure with ``ufo_graph_save_to_json`` and read back
+again with ``ufo_graph_read_from_json``.
+
+The ``Ufo.Scheduler`` on the other hand is an implementation of a strategy *how*
+to execute the filters contained in a graph. Therefore, the scheduler is passed
+a graph object on execution.
+
+Configuration
+=============
+
+There are two different notions of configuration in the Ufo framework: per-node
+configuration and execution configuration. The former is realized with GObject
+properties. In Python, these properties can be set as a named parameter with
+``set_properties`` or assigned to the property as part of the ``props``::
+
+    writer = pm.get_filter('reader')
+
+    writer.props.prefix = 'foo'
+    writer.set_properties(prefix='foo')
+
+The execution configuration is independent of the parameters of the actual
+computation and used to determine environment specific foos. For example, if the
+filters are not installed system wide, there need to be a way to tell the
+framework were these are located. This information is stored in an
+``Ufo.Configuration`` object. Each part of the framework that implements the
+``Ufo.Configurable`` interface accepts such an object at construction time and
+uses necessary information stored within::
+
+    # Lets assume that filters and .cl files are stored in the parent directory.
+    # So we create a new configuration object and set its `paths' property.
+    config = Ufo.Configuration(paths=['..'])
+
+    # The PluginManager is configurable ...
+    pm = Ufo.PluginManager(configuration=config)
+
+    # ... so is the scheduler
+    scheduler = Ufo.Scheduler(configuration=config)
+
+
+Profiling
+=========
+
+Profiling is disabled by default but can be enabled with the ``profile-level``
+property of a configuration object. This property receives values from the
+``UfoProfilerLevel`` flags enum ::
+
+    # track only OpenCL events
+    config = Ufo.Configuration(profile_level=Ufo.ProfilerLevel.OPENCL)
+
+    scheduler = Ufo.Scheduler(configuration=config)
+
+If you do not specify an output file name (``profile_output`` property), the
+profiling information is output to ``stdout``.
+
+The profiling information can be analysed with the ``clprof`` tool, as part of
+the standard distribution::
+
+    $ clprof stats
+    Kernel               Submit Delay   Exec Delay  Kernel Exec   Total Exec   Queue Dist
+    -------------------------------------------------------------------------------------
+    filter                     0.0033       1.0551       0.0491       0.4915          1.0
+    fft_spread                 0.0079       1.0265       0.0398       0.3982          1.0
+    backproject_tex            0.0022       0.5808       4.9480      49.4805          1.0
+    fft_pack                   0.0034       0.1187       0.0311       0.3113          1.0
+
+The output is the averaged time in milli seconds for submission delay, execution
+delay and kernel execution:
+
+* *Submission delay*: time between calling the kernel and actually submission
+  into the command queue
+* *Execution delay*: time between enqueueing and execution of the kernel
+* *Execution*: time for executing the kernel
+
+Moreover, the total execution time in milli seconds and the kernel distribution
+among the command queues is shown.
diff --git a/docs/manual/using/cluster.rst b/docs/manual/using/cluster.rst
new file mode 100644
index 0000000..66ac63a
--- /dev/null
+++ b/docs/manual/using/cluster.rst
@@ -0,0 +1,23 @@
+.. _using-cluster:
+
+==========================
+Running tasks in a cluster
+==========================
+
+The UFO framework comes with built-in cluster capabilities based on ZeroMQ 3.2.
+Contrary to bulk cluster approaches (e.g. solving large linear systems), UFO
+tries to distribute `streamed` data on a set of multiple machines. On each
+remote slave, ``ufod`` must be started. By default, the server binds to port
+5555 on any available network adapter. To change this, use the ``-l/--listen``
+option::
+    
+    $ ufod --listen tcp://ib0:5555
+
+will let ``ufod`` use the first Infiniband-over-IP connection.
+
+On the master host, you pass the remote slave addresses to the scheduler object.
+In Python this would look like this::
+
+    sched = Ufo.Scheduler(remotes=['tcp://foo.bar.org:5555'])
+
+Address are notated according to `ZeroMQ <http://api.zeromq.org/3-2:zmq-tcp>`_.
diff --git a/docs/manual/using/filters.rst b/docs/manual/using/filters.rst
new file mode 100644
index 0000000..57f26b7
--- /dev/null
+++ b/docs/manual/using/filters.rst
@@ -0,0 +1,244 @@
+.. _filters:
+
+=====
+Tasks
+=====
+
+.. default-domain:: c
+
+UFO filters are simple shared objects that expose their ``GType`` and implement
+the :type:`UfoFilter` class.
+
+
+Writing a task in C
+===================
+
+.. highlight:: bash
+
+Writing a new UFO filter is simple and by calling ::
+
+    ./mkfilter.py --name AwesomeFoo --type TYPE
+
+in the ``tools/`` directory, you can avoid writing that tedious GObject boiler
+plate code on your own. The name is a camel-cased version of your new filter.
+Type must be one of
+
+* ``cpu``: For a CPU-based task
+* ``gpu``: For a CPU-based task
+
+You are now left with two files ``ufo-awesome-foo-task.c`` and
+``ufo-awesome-foo-task.h``. If you intend to distribute that filter with the
+main UFO filter distribution, copy these files to ``ufo-filters/src``. If you
+are not depending on any third-party library you can just add the following line
+to the ``CMakeLists.txt`` file::
+
+    set(ufofilter_SRCS
+        ...
+        ufo-awesome-foo-task.c
+        ...)
+
+You can compile this as usual by typing ::
+
+    make
+
+in the CMake build directory of ``ufo-filters``.
+
+
+Initializing filters
+--------------------
+
+.. highlight:: c
+
+Regardless of filter type, the ``ufo_awesome_foo_task_init()`` method is the
+*constructor* of the filter object and the best place to setup all data that
+does not depend on any input data.
+
+The ``ufo__awesome_foo_task_class_init()`` method on the other hand ist the
+*class constructor* that is used to *override* virtual methods by setting
+function pointers in each classes' vtable.
+
+You *must* override the following methods: ``ufo_awesome_task_get_structure``,
+``ufo_awesome_task_setup``, ``ufo_awesome_task_get_requisition`` and
+``ufo_awesome_task_process``.
+
+``get_structure`` is called by the run-time in order to determine how many
+inputs your task expects, which dimensions are allowed on each input and what
+processing mode your task runs ::
+
+    static void
+    ufo_awesome_task_get_structure (UfoTask *task,
+                                    guint *n_inputs,
+                                    UfoInputParam **in_params,
+                                    UfoTaskMode *mode)
+    {
+        *mode = UFO_TASK_MODE_SINGLE;
+        *n_inputs = 1;
+        *in_params = g_new0 (UfoInputParam, 1);
+        (*in_params)[0].n_dims = 2;
+    }
+
+This task expects one two-dimensional input. Be aware, that the callee must
+allocate memory for the ``in_params`` data structure.
+
+``setup`` can be used to initialize data that depends on run-time resources like
+OpenCL contexts etc. This method is called only *once* ::
+
+    static void
+    ufo_awesome_task_setup (UfoTask *task,
+                            UfoResources *resources,
+                            GError **error)
+    {
+        cl_context context;
+
+        context = ufo_resources_get_context (resources);
+
+        /*
+           Do something with the context like allocating buffers or create
+           kernels.
+         */
+    }
+
+On the other hand, ``get_requisition`` is called on each iteration right before
+``process``. It is used to determine which size an output buffer must have
+depending on the inputs. For this you must fill in the ``requisition`` structure
+correctly. If our output buffer needs to be as big as our input buffer we would
+specify ::
+
+    static void
+    ufo_awesome_task_get_requisition (UfoTask *task,
+                                      UfoBuffer **inputs,
+                                      UfoRequisition *requisition)
+    {
+        ufo_buffer_get_requisition (inputs[0], requisition);
+    }
+
+Finally, you have to override the ``process`` method. Note, that the function
+signatures differ for GPU and CPU tasks ::
+
+    static gboolean
+    ufo_awesome_task_process (UfoGpuTask *task,
+                              UfoBuffer **inputs,
+                              UfoBuffer *output,
+                              UfoRequisition *requisition,
+                              UfoGpuNode *node)
+    {
+        /* Now we can request cl_mem or float arrays from in and outputs. */
+        cl_command_queue cmd_queue;
+        cl_mem host_in;
+        cl_mem host_out;
+
+        cmd_queue = ufo_gpu_node_get_cmd_queue (node);
+        host_in = ufo_buffer_get_device_array (inputs[0], cmd_queue);
+        host_out = ufo_buffer_get_device_array (output, cmd_queue);
+
+        /* Call a kernel or do other meaningful work. */
+    }
+
+Tasks can and will be copied to speed up the computation on multi-GPU systems.
+Any parameters that are accessible from the outside via a property are
+automatically copied by the run-time system. To copy private data that is only
+visible at the file scope, you have to override the ``UFO_NODE_CLASS`` method
+``copy`` and copy the data yourself. This method is *always* called before
+``setup`` so you can be assured to re-create your private data on the copied
+task.
+
+.. note::
+
+    It is strongly encouraged that you export all your parameters as properties
+    and re-build any internal data structures off of these parameters.
+
+
+Additional source files
+-----------------------
+
+For modularity reasons, you might want to split your filter sources into
+different compilation units. In order to compile and link them against the
+correct library, add the following statements to the ``src/CMakeLists.txt``
+file ::
+
+    set(awesome_foo_misc_SRCS foo.c bar.c baz.c)
+
+in case your filter is still called ``AwesomeFoo``. Notice, that the variable
+name matches the plugin name with underscores between the lower-cased letters.
+
+
+Writing point-based OpenCL filters
+----------------------------------
+
+.. highlight:: c
+
+For point-based image operations it is much faster to use the cl-plugin that
+writing a full-fledged C filter. We create a new file ``simple.cl``, that
+contains a simple kernel that inverts our normalized input (you can silently
+ignore the ``scratch`` parameter for now)::
+
+    __kernel void invert(__global float *data, __local float *scratch)
+    {
+        /* where are we? */
+        int index = get_global_id(1) * get_global_size(0) + get_global_id(0);
+        float inverted_value = 1.0f - data[index];
+        data[index] = inverted_value;
+    }
+
+.. highlight:: python
+
+We wire this small kernel into this short Python script::
+
+    from gi.repository import Ufo
+
+    pm = Ufo.PluginManager()
+    reader = pm.get_filter('reader')
+    writer = pm.get_filter('writer')
+
+    # this filter applies the kernel
+    cl = pm.get_filter('opencl')
+    cl.set_properties(file='simple.cl', kernel='invert')
+
+    g = Ufo.Graph()
+    g.connect_filters(reader, cl)
+    g.connect_filters(cl, writer)
+
+    s = Ufo.Scheduler()
+    s.run(g)
+
+For more information on how to write OpenCL kernels, consult the official
+`OpenCL reference pages`__.
+
+__ http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/
+
+
+The GObject property system
+===========================
+
+.. _filters-block:
+
+Wait until a property satisfies a condition
+-------------------------------------------
+
+.. highlight:: c
+
+For some filters it could be important to not only wait until input buffers
+arrive but also properties change their values. For example, the back-projection
+should only start as soon as it is assigned a correct center-of-rotation. To
+implement this, we have to define a condition function that checks if a
+``GValue`` representing the current property satisfies a certain condition ::
+
+    static gboolean is_larger_than_zero(GValue *value, gpointer user_data)
+    {
+        return g_value_get_float(value) > 0.0f;
+    }
+
+As the filter installed the properties it also knows which type it is and which
+``g_value_get_*()`` function to call. Now, we wait until this conditions holds
+using :c:func:`ufo_filter_wait_until` ::
+
+    /* Somewhere in ufo_filter_process() */
+    ufo_filter_wait_until(self, properties[PROP_CENTER_OF_ROTATION],
+            &is_larger_than_zero, NULL);
+
+.. warning::
+
+    :c:func:`ufo_filter_wait_until` might block indefinitely when the
+    condition function never returns ``TRUE``.
+
+.. seealso:: :ref:`faq-synchronize-properties`
diff --git a/docs/manual/using/index.rst b/docs/manual/using/index.rst
new file mode 100644
index 0000000..79ce8c3
--- /dev/null
+++ b/docs/manual/using/index.rst
@@ -0,0 +1,30 @@
+.. _using-ufo:
+
+=========
+Using UFO
+=========
+
+UFO is a framework for high-speed image processing at Synchrotron_ beamlines. It
+facilitates every available hardware device to process tomographic data as fast
+as possible with on-line reconstruction as the ultimate goal.
+
+.. _Synchrotron: http://en.wikipedia.org/wiki/Synchrotron
+
+It is written in C using the GLib_ and GObject_ libraries to provide an
+object-oriented :ref:`API <ufo-api>`.
+
+.. _GLib: http://developer.gnome.org/glib/
+.. _GObject: http://developer.gnome.org/gobject/stable/index.html
+
+After :ref:`installing <installation>` the framework you're ready to build your
+own image processing pipeline or implement a new filter.
+
+
+.. toctree::
+    :maxdepth: 2
+
+    quickstart.rst
+    background.rst
+    cluster.rst
+    filters.rst
+    
diff --git a/docs/manual/using/quickstart.rst b/docs/manual/using/quickstart.rst
new file mode 100644
index 0000000..e807c13
--- /dev/null
+++ b/docs/manual/using/quickstart.rst
@@ -0,0 +1,195 @@
+.. _using-hello-world:
+
+=================
+Quick start guide
+=================
+
+There are essentially two ways to specify and execute a graph of tasks. One
+involves writing a :ref:`JSON file <json-configuration>` that is executed by the
+``runjson`` utility, the other way uses the provided language bindings to setup
+the task graph specifically.
+
+
+Using a JSON description
+========================
+
+The custom JSON format, described :ref:`here <json-configuration>`, has the
+advantage to be language-agnostic and portable across different versions of the
+UFO framework. Let's start with a simple example, that computes the
+one-dimensional Fourier-transform on a set of input files::
+
+    {
+        "nodes" : [
+            {
+                "plugin": "reader",
+                "name": "reader",
+                "properties" : { "path": "*.tif" }
+            },
+            {
+                "plugin": "fft",
+                "name": "fft"
+            }
+            {
+                "plugin": "writer",
+                "name": "writer",
+                "properties" : { "prefix": "fft-" }
+            }
+        ],
+        "edges" : [
+            {
+                "from": { "name": "reader" },
+                "to": { "name": "fft" }
+            },
+            {
+                "from": { "name": "fft" },
+                "to": { "name": "writer" }
+            }
+        ]
+    }
+
+Save this to a file named ``fft.json`` and execute it by calling the ``runjson``
+tool::
+
+    $ runjson fft.json
+
+``runjson`` takes two optional parameters. ``-p`` or ``--path`` expects a path
+name to a location where UFO plugins are stored. This can be useful if the
+standard nodes were installed in a user-defined location or third-party nodes
+should be looked up too.
+
+The ``-a`` or ``--address`` parameter expects a ZeroMQ-conform address where a
+``ufod`` server is running. Part of the work is then distributed to that
+machine. For more information, read up on :ref:`clustering <using-cluster>`.
+
+
+C interface
+===========
+
+.. highlight:: c
+
+A simple UFO program written in C that loads the JSON description can look like
+this::
+
+    /* ufo/ufo.h is the only header allowed to be included */
+    #include <ufo/ufo.h>
+
+    int main (void)
+    {
+        UfoTaskGraph *graph;
+        UfoScheduler *scheduler;
+        UfoPluginManager *manager;
+
+        g_type_init ();  /* you _must_ call this! */
+
+        graph = ufo_graph_new ();
+        manager = ufo_plugin_manager_new ();
+
+        ufo_task_graph_read_from_json (graph, manager, "hello-world.json", NULL);
+
+        scheduler = ufo_scheduler_new (NULL, NULL);
+        ufo_scheduler_run (scheduler, graph, NULL);
+
+        /* Destroy all objects */
+        g_object_unref (graph);
+        g_object_unref (scheduler);
+        g_object_unref (manager);
+        return 0;
+    }
+
+.. highlight:: bash
+
+You can compile this with::
+
+    $ gcc `pkg-config --cflags --libs ufo` foo.c -o foo
+
+As you can see we simply construct a new ``UfoGraph`` object from a JSON encoded
+:ref:`configuration file <json-configuration>` and execute the computation
+pipeline with a ``UfoScheduler`` object.
+
+.. highlight:: c
+
+Rather than loading the structure from a file, you can also construct it by
+hand::
+
+    int main(void)
+    {
+        UfoGraph *graph;
+        UfoPluginManager *manager;
+        UfoScheduler *scheduler;
+        UfoNode *reader;
+        UfoNode *writer;
+
+        g_type_init ();  /* you _must_ call this! */
+
+        graph = ufo_graph_new ();
+        manager = ufo_plugin_manager_new (NULL);
+        scheduler = ufo_scheduler_new (NULL, NULL);
+        reader = ufo_plugin_manager_get_task (manager, "reader", NULL);
+        writer = ufo_plugin_manager_get_task (manager, "writer", NULL);
+
+        g_object_set (G_OBJECT (reader),
+                      "path", "/home/user/data/*.tif",
+                      "count", 5,
+                      NULL);
+
+        ufo_graph_connect_nodes (graph, reader, writer, NULL);
+        ufo_scheduler_run (scheduler, graph, NULL);
+        return 0;
+    }
+
+
+Python Interface
+================
+
+There are no plans to support any languages with manually written language
+bindings. However, UFO is a GObject-based library from which ``gir`` (GObject
+Introspection) files can be generated at build time. Any language that supports
+GObject Introspection and the ``gir``/``typelib`` format is thus able to
+integrate UFO. No manual intervention is need if the GObject Introspection tools
+are found.
+
+Because several languages support GObject Introspection, you have to consult the
+appropriate reference manuals to find out how the GObjects are mapped to their
+language equivalents. Some of the options are
+
+- Python: PyGObject_
+- Javascript: Gjs_ and Seed_
+- Vala has direct support using the ``--pkg`` option
+
+.. _PyGObject: http://live.gnome.org/PyGObject
+.. _Gjs: http://live.gnome.org/Gjs
+.. _Seed: http://live.gnome.org/Seed
+
+A `GNOME wiki page`__ lists all available runtime bindings.
+
+__ http://live.gnome.org/GObjectIntrospection/Users
+
+.. highlight:: python
+
+The simple example from the beginning -- with Python-GObject installed -- would
+look like this::
+
+    from gi.repository import Ufo
+
+    manager = Ufo.PluginManager()
+    graph = Ufo.TaskGraph()
+    scheduler = Ufo.Scheduler()
+
+    graph.read_from_json(manager, "some-graph.json")
+    scheduler.run(graph)
+
+Similarly, constructing the graph by hand maps one-to-one to the Python object
+and keyword system::
+
+    from gi.repository import Ufo
+
+    graph = Ufo.Graph()
+    manager = Ufo.PluginManager()
+    scheduler = Ufo.Scheduler()
+
+    reader = manager.get_task('reader')
+    writer = manager.get_task('writer')
+    reader.set_properties(path='/home/user/data/*.tif', count=5)
+
+    graph.connect_nodes(reader, writer)
+    scheduler.run(graph)
diff --git a/docs/manual/whatsnew/0.1.rst b/docs/manual/whatsnew/0.1.rst
new file mode 100644
index 0000000..7499916
--- /dev/null
+++ b/docs/manual/whatsnew/0.1.rst
@@ -0,0 +1,12 @@
+==========================
+What's New in ufo-core 0.1
+==========================
+
+:Author: Matthias Vogelgesang
+
+Changes from version 0.1.0 to 0.1.1
+===================================
+
+Fixed bugs
+----------
+    - Ticket #55: tests/test-channel blocks indefinitely
diff --git a/docs/manual/whatsnew/0.2.rst b/docs/manual/whatsnew/0.2.rst
new file mode 100644
index 0000000..e6dc9a3
--- /dev/null
+++ b/docs/manual/whatsnew/0.2.rst
@@ -0,0 +1,69 @@
+==========================
+What's New in ufo-core 0.2
+==========================
+
+Major breakage
+==============
+
+- Filters are now prefixed again with ``libfilter`` to allow introspected
+  documentation. Thus, any filter built for 0.1 cannot be used because they are
+  simply not found.
+
+- :c:func:`ufo_plugin_manager_get_filter` received a new third parameter
+  ``error`` that reports errors when opening and loading a UfoFilter from a
+  shared object.
+
+- :c:func:`ufo_resource_manager_add_program` is removed.
+
+- The kernel file name must be passed to :c:func:`ufo_resource_manager_get_kernel`.
+
+- The ``CHECK_ERROR`` macro defined in ``ufo-resource-manager.h`` was renamed to
+  ``CHECK_OPENCL_ERROR`` to better reflect its real purpose.
+
+- The old JSON specification has been changed to reflect the possibilities of
+  the current API. Thus, JSON files that worked under Ufo 0.1 cannot be read
+  with Ufo 0.2.
+
+- Removed the otherwise unused :c:func:`ufo_buffer_get_transfer_time` and
+  replaced this with the more flexible :c:func:`ufo_buffer_get_transfer_timer`.
+
+- Rename :c:func:`ufo_filter_initialize` to
+  :c:func:`ufo_filter_set_plugin_name` that reflects its true meaning.
+
+Scheduling
+----------
+
+A more scheduable way to run filters has been implemented with the virtual
+:c:func:`process_cpu` and :c:func:`process_gpu` methods. Contrary to the old
+way, they act on *one* working set at a time that is passed as an array of
+pointers to :c:type:`UfoBuffer`. Sometimes, a filter needs to setup data
+depending on the input size. For this reason, the virtual method
+:c:func:`initialize` takes a second parameter that is again a list of pointers
+to buffer objects.
+
+Moreover, the :c:type:`UfoScheduler` class has been added that is combining
+the work previously accomplished by :c:func:`ufo_filter_process` and
+:c:func:`ufo_graph_run`. The scheduler orchestrates the filters and
+assigns resources in a meaningful way.
+
+If written in the new kernel style, producer filters must return a boolean flag
+denoting if data was produced or not.
+
+
+General improvements
+====================
+
+- The manual was restructured considerably.
+
+- Saving graphs as JSON files has been added via
+  :c:func:`ufo_graph_save_to_json()`.
+
+- Filters can now wait until their properties satisfy a condition using
+  :c:func:`ufo_filter_wait_until`, see also :ref:`filters-block`.
+
+- A new method :c:func:`ufo_resource_manager_get_kernel_from_source` so that
+  filters can load kernels directly from source.
+
+- Streamlined error handling: Filters should not issue ``g_warnings`` or
+  ``g_errors`` on their own anymore but create an error with ``g_error_new`` and
+  return that.
diff --git a/docs/manual/whatsnew/0.3.rst b/docs/manual/whatsnew/0.3.rst
new file mode 100644
index 0000000..51f3201
--- /dev/null
+++ b/docs/manual/whatsnew/0.3.rst
@@ -0,0 +1,31 @@
+==========================
+What's New in ufo-core 0.3
+==========================
+
+Major breakage
+==============
+
+- A graph is now a simple data structure and not a specific graph of task nodes.
+  This is implemented by a TaskGraph.
+
+- Filters are now called TaskNodes and connected with
+  ``ufo_task_graph_connect_nodes`` and ``ufo_task_graph_connect_nodes_full``
+  respectively.
+
+
+Graph expansion
+===============
+
+With 0.2, Using multiple GPUs was possible by manually splitting paths in the
+graph and assigning GPUs. Now, task graphs are automatically expanded depending
+on the number of available GPUs and remote processing slaves that are started
+with the newly added ``ufod`` server software.
+
+
+Minor improvements
+==================
+
+- A ``deploy.sh`` script has been added for easier deployment of the software
+  stack. This is especially useful to install everything in the home directory
+  of the user, who only needs to setup ``LD_LIBRARY_PATH`` and
+  ``GI_TYPELIB_PATH`` correctly to run the software.
diff --git a/docs/manual/whatsnew/index.rst b/docs/manual/whatsnew/index.rst
new file mode 100644
index 0000000..f02388c
--- /dev/null
+++ b/docs/manual/whatsnew/index.rst
@@ -0,0 +1,15 @@
+.. _whatsnew-index:
+
+======================
+What's New in ufo-core
+======================
+
+The "What's New in ufo-core" series documents the most important changes between
+major versions of the UFO core package.
+
+.. toctree::
+    :maxdepth: 2
+
+    0.3.rst
+    0.2.rst
+    0.1.rst
diff --git a/docs/scangobj.sh.in b/docs/scangobj.sh.in
new file mode 100644
index 0000000..e58f901
--- /dev/null
+++ b/docs/scangobj.sh.in
@@ -0,0 +1 @@
+LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${CMAKE_CURRENT_BINARY_DIR}" CFLAGS="${GTK_DOC_CFLAGS}" LDFLAGS="${GTK_DOC_LDFLAGS} -L${CMAKE_CURRENT_BINARY_DIR} -lufo" gtkdoc-scangobj --module=Ufo > /dev/null
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
new file mode 100644
index 0000000..f93a571
--- /dev/null
+++ b/python/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 2.6)
+
+# --- Find packages and libraries ---------------------------------------------
+find_program(PYTHON "python")
+
+if (PYTHON)
+    option(WITH_PYTHON_TOOLS "Build Python tools module" ON)
+
+    if (WITH_PYTHON_TOOLS)
+        set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in")
+        set(SETUP_PY    "${CMAKE_CURRENT_BINARY_DIR}/setup.py")
+        set(DEPS        "${CMAKE_CURRENT_SOURCE_DIR}/ufotools/__init__.py")
+        set(OUTPUT      "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp")
+
+        configure_file(${SETUP_PY_IN} ${SETUP_PY})
+
+        add_custom_command(
+            OUTPUT ${OUTPUT}
+            COMMAND ${PYTHON} setup.py build
+            COMMAND ${CMAKE_COMMAND} -E touch ${OUTPUT}
+            DEPENDS ${DEPS}
+            COMMENT "Build ufotools"
+        )
+
+        add_custom_target(ufotools ALL DEPENDS ${OUTPUT})
+
+        install(CODE "execute_process(COMMAND ${PYTHON} ${SETUP_PY} install)")
+    endif()
+endif()
diff --git a/python/setup.py.in b/python/setup.py.in
new file mode 100644
index 0000000..21bd87d
--- /dev/null
+++ b/python/setup.py.in
@@ -0,0 +1,13 @@
+from distutils.core import setup, Extension
+
+if __name__ == '__main__':
+    setup(name='ufotools',
+          version='${PACKAGE_VERSION}',
+          author='Matthias Vogelgesang',
+          author_email='matthias.vogelgesang at kit.edu',
+          url='http://ufo.kit.edu',
+          license='GPL v3',
+          description='ufo extension module',
+          long_description='ufo extension module',
+          package_dir={ '': '${CMAKE_CURRENT_SOURCE_DIR}' },
+          packages=['ufotools'])
diff --git a/python/ufotools/__init__.py b/python/ufotools/__init__.py
new file mode 100644
index 0000000..30ecdf7
--- /dev/null
+++ b/python/ufotools/__init__.py
@@ -0,0 +1,27 @@
+class CallableFilter(object):
+    def __init__(self, gobject, graph):
+        self.gobject = gobject
+        self.graph = graph
+        self.name = gobject.get_plugin_name()
+
+    def __call__(self, *args, **kwargs):
+        n_inputs = self.gobject.get_num_inputs()
+
+        if n_inputs != len(args):
+            raise TypeError("`%s' receives only %i argument(s) but was called with %i parameters" % (self.name, n_inputs, len(args)))
+
+        for i, target in enumerate(args):
+            self.graph.connect_filters_full(target.gobject, 0, self.gobject, i, Ufo.TransferMode.DISTRIBUTE)
+
+        return self
+
+
+class NodeFactory(object):
+    def __init__(self, plugin_manager, graph):
+        self.pm = plugin_manager
+        self.graph = graph
+
+    def get(self, name, **kwargs):
+        gobject = self.pm.get_filter(name)
+        gobject.set_properties(kwargs)
+        return CallableFilter(gobject, graph)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..84736dd
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 2.8)
+
+# configure unit tests
+set(TEST_SRCS
+    test-suite.c
+    #test-buffer.c
+    #test-channel.c
+    test-config.c
+    #test-filter.c
+    #test-filter-direct.c
+    test-graph.c
+    test-profiler.c
+    )
+
+set(SUITE_BIN "test-suite")
+
+option(WITH_TESTS "Build test suite" ON)
+
+if (WITH_TESTS)
+    include_directories(
+        {CMAKE_CURRENT_SOURCE_DIR})
+
+    add_executable(${SUITE_BIN} ${TEST_SRCS})
+
+    target_link_libraries(${SUITE_BIN}
+        ufo
+        ${UFOCORE_DEPS})
+
+    add_test(${SUITE_BIN} ${SUITE_BIN})
+
+    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/gtester.xsl"
+        "${CMAKE_CURRENT_BINARY_DIR}/gtester.xsl" @ONLY IMMEDIATE)
+endif()
diff --git a/tests/gtester.xsl b/tests/gtester.xsl
new file mode 100644
index 0000000..8aee6a8
--- /dev/null
+++ b/tests/gtester.xsl
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+    <!-- I can't believe I have to do this -->
+    <!-- Based on this code:
+            http://geekswithblogs.net/Erik/archive/2008/04/01/120915.aspx
+    -->
+    <xsl:template name="strreplace">
+        <xsl:param name="string"/>
+        <xsl:param name="token"/>
+        <xsl:param name="newtoken"/>
+        <xsl:choose>
+            <xsl:when test="contains($string, $token)">
+                <xsl:value-of select="substring-before($string, $token)"/>
+                <xsl:value-of select="$newtoken"/>
+                <xsl:call-template name="strreplace">
+                    <xsl:with-param name="string" select="substring-after($string, $token)"/>
+                    <xsl:with-param name="token" select="$token"/>
+                    <xsl:with-param name="newtoken" select="$newtoken"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="$string"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template match="/">
+        <xsl:for-each select="gtester">
+            <testsuite>
+                <!-- currently we do not support different test binaries and only take the path
+                     of the first as the testsuite name -->
+                <xsl:attribute name="name">
+                    <xsl:value-of select="testbinary[1]/@path"/>
+                </xsl:attribute>
+                <xsl:attribute name="tests">
+                    <xsl:value-of select="count(testbinary/testcase[not(@skipped='1')])"/>
+                </xsl:attribute>
+                <xsl:attribute name="time">
+                    <xsl:value-of select="sum(testbinary/duration)"/>
+                </xsl:attribute>
+                <xsl:attribute name="failures">
+                    <xsl:value-of select="count(testbinary/testcase/status[@result='failed'])"/>
+                </xsl:attribute>
+                <xsl:for-each select="testbinary/testcase[not(@skipped='1')]">
+                    <testcase>
+                        <xsl:variable name="classname">
+                            <xsl:call-template name="strreplace">
+                                <xsl:with-param name="string" select="substring-after(@path, '/')"/>
+                                <xsl:with-param name="token" select="'/'"/>
+                                <xsl:with-param name="newtoken" select="'.'"/>
+                            </xsl:call-template>
+                        </xsl:variable>
+                        <xsl:attribute name="classname">
+                            <xsl:value-of select="$classname"/>
+                        </xsl:attribute>
+                        <xsl:attribute name="name">g_test</xsl:attribute>
+                        <xsl:attribute name="time">
+                            <xsl:value-of select="duration"/>
+                        </xsl:attribute>
+                        <xsl:if test="status[@result = 'failed']">
+                            <failure>
+                                <xsl:value-of select="error"/>
+                            </failure>
+                        </xsl:if>
+                    </testcase>
+                </xsl:for-each>
+            </testsuite>
+        </xsl:for-each>
+    </xsl:template>
+</xsl:stylesheet>
diff --git a/tests/test-config.c b/tests/test-config.c
new file mode 100644
index 0000000..1f479e8
--- /dev/null
+++ b/tests/test-config.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ufo.h>
+#include "test-suite.h"
+
+static void
+test_path (void)
+{
+    UfoConfig *config;
+    GObject     *object;
+    GValueArray *array;
+    GValue       value = {0};
+    const gchar *p1 = "/usr/foo/bar";
+    const gchar *p2 = "/home/user/foo";
+    GList *paths;
+
+    /* Initialize value array with the two static strings */
+    array = g_value_array_new (2);
+    g_value_init (&value, G_TYPE_STRING);
+    g_value_set_string (&value, p1);
+    g_value_array_append (array, &value);
+
+    g_value_reset (&value);
+    g_value_set_string (&value, p2);
+    g_value_array_append (array, &value);
+
+    /* Create a new object object and see what happens */
+    object = g_object_new (UFO_TYPE_CONFIG, "paths", array, NULL);
+
+    g_value_array_free (array);
+
+    g_object_get (object,
+                  "paths", &array,
+                  NULL);
+
+    g_assert (array->n_values == 2);
+
+    /* Check that paths match the ones we added */
+    g_assert (g_strcmp0 (p1, g_value_get_string (g_value_array_get_nth (array, 0))) == 0);
+    g_assert (g_strcmp0 (p2, g_value_get_string (g_value_array_get_nth (array, 1))) == 0);
+
+    g_value_array_free (array);
+
+    /* Now check the C API */
+    config = UFO_CONFIG (object);
+    paths = ufo_config_get_paths (config);
+
+    g_assert (paths != NULL);
+    g_assert (g_list_length (paths) >= 2);
+    g_assert (g_strcmp0 (p1, g_list_nth_data (paths, 0)) == 0);
+    g_assert (g_strcmp0 (p2, g_list_nth_data (paths, 1)) == 0);
+
+    g_list_free (paths);
+    g_object_unref (object);
+}
+
+static void
+test_path_not_set (void)
+{
+    UfoConfig *config;
+    GList *paths;
+
+    config = ufo_config_new ();
+    paths = ufo_config_get_paths (config);
+    g_assert (paths != NULL);
+    g_list_free (paths);
+    g_object_unref (config);
+}
+
+void
+test_add_config (void)
+{
+    g_test_add_func("/config/path",
+                    test_path);
+
+    /* Check trac ticket #127 */
+    g_test_add_func("/config/path-not-set",
+                    test_path_not_set);
+}
diff --git a/tests/test-graph.c b/tests/test-graph.c
new file mode 100644
index 0000000..e9759ec
--- /dev/null
+++ b/tests/test-graph.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ufo.h>
+#include "test-suite.h"
+
+typedef struct {
+    UfoGraph *graph;
+    UfoGraph *sequence;
+    UfoNode *root;
+    UfoNode *target1;
+    UfoNode *target2;
+} Fixture;
+
+static gpointer FOO_LABEL = GINT_TO_POINTER (0xDEADF00D);
+static gpointer BAR_LABEL = GINT_TO_POINTER (0xF00BA);
+static gpointer BAZ_LABEL = GINT_TO_POINTER (0xBA22BA22);
+
+static void
+fixture_setup (Fixture *fixture, gconstpointer data)
+{
+    fixture->graph = ufo_graph_new ();
+    g_assert (UFO_IS_GRAPH (fixture->graph));
+
+    fixture->sequence = ufo_graph_new ();
+
+    ufo_graph_register_node_type (fixture->graph, UFO_TYPE_NODE);
+    ufo_graph_register_node_type (fixture->sequence, UFO_TYPE_NODE);
+
+    fixture->root = ufo_node_new (FOO_LABEL);
+    fixture->target1 = ufo_node_new (BAR_LABEL);
+    fixture->target2 = ufo_node_new (BAZ_LABEL);
+
+    ufo_graph_connect_nodes (fixture->graph,
+                             fixture->root,
+                             fixture->target1,
+                             FOO_LABEL);
+
+    ufo_graph_connect_nodes (fixture->graph,
+                             fixture->root,
+                             fixture->target2,
+                             BAR_LABEL);
+
+    ufo_graph_connect_nodes (fixture->sequence,
+                             fixture->root,
+                             fixture->target1,
+                             BAR_LABEL);
+
+    ufo_graph_connect_nodes (fixture->sequence,
+                             fixture->target1,
+                             fixture->target2,
+                             FOO_LABEL);
+}
+
+static void
+fixture_teardown (Fixture *fixture, gconstpointer data)
+{
+    g_object_unref (fixture->graph);
+    g_object_unref (fixture->sequence);
+}
+
+static void
+test_connected (Fixture *fixture, gconstpointer data)
+{
+    g_assert (ufo_graph_is_connected (fixture->sequence,
+                                      fixture->root,
+                                      fixture->target1));
+    g_assert (ufo_graph_is_connected (fixture->sequence,
+                                      fixture->target1,
+                                      fixture->target2));
+    g_assert (!ufo_graph_is_connected (fixture->sequence,
+                                       fixture->root,
+                                       fixture->target2));
+    g_assert (!ufo_graph_is_connected (fixture->sequence,
+                                       fixture->target1,
+                                       fixture->root));
+    g_assert (!ufo_graph_is_connected (fixture->sequence,
+                                       fixture->target2,
+                                       fixture->root));
+    g_assert (!ufo_graph_is_connected (fixture->sequence,
+                                       fixture->target2,
+                                       fixture->target1));
+}
+
+static void
+test_get_roots (Fixture *fixture, gconstpointer data)
+{
+    GList *roots;
+
+    roots = ufo_graph_get_roots (fixture->graph);
+    g_assert (g_list_length (roots) == 1);
+    g_assert (g_list_nth_data (roots, 0) == fixture->root);
+    g_list_free (roots);
+}
+
+static void
+test_get_num_nodes (Fixture *fixture, gconstpointer data)
+{
+    g_assert (ufo_graph_get_num_nodes (fixture->graph) == 3);
+    g_assert (ufo_graph_get_num_nodes (fixture->sequence) == 3);
+}
+
+static void
+test_get_num_edges (Fixture *fixture, gconstpointer data)
+{
+    g_assert (ufo_graph_get_num_edges (fixture->graph) == 2);
+    g_assert (ufo_graph_get_num_edges (fixture->sequence) == 2);
+}
+
+static void
+test_get_edges (Fixture *fixture, gconstpointer data)
+{
+    GList *edges;
+    UfoEdge *edge;
+
+    edges = ufo_graph_get_edges (fixture->graph);
+    g_assert (g_list_length (edges) == 2);
+
+    edge = g_list_nth_data (edges, 0);
+    g_assert (edge->source == fixture->root);
+    g_assert (edge->target == fixture->target1 ||
+              edge->target == fixture->target2);
+
+    edge = g_list_nth_data (edges, 1);
+    g_assert (edge->source == fixture->root);
+    g_assert (edge->target == fixture->target1 ||
+              edge->target == fixture->target2);
+
+    g_list_free (edges);
+}
+
+static void
+test_get_successors (Fixture *fixture, gconstpointer data)
+{
+    GList *successors;
+
+    successors = ufo_graph_get_successors (fixture->sequence, fixture->target1);
+    g_assert (g_list_length (successors) == 1);
+    g_assert (g_list_nth_data (successors, 0) == fixture->target2);
+    g_list_free (successors);
+}
+
+static void
+test_get_predecessors (Fixture *fixture, gconstpointer data)
+{
+    GList *predecessors;
+
+    predecessors = ufo_graph_get_predecessors (fixture->sequence, fixture->target2);
+    g_assert (g_list_length (predecessors) == 1);
+    g_assert (g_list_nth_data (predecessors, 0) == fixture->target1);
+    g_list_free (predecessors);
+}
+
+static void
+test_remove_edge (Fixture *fixture, gconstpointer data)
+{
+    GList *successors;
+
+    ufo_graph_remove_edge (fixture->sequence, fixture->target1, fixture->target2);
+    successors = ufo_graph_get_successors (fixture->sequence, fixture->target1);
+    g_assert (successors == NULL);
+    g_assert (g_list_length (successors) == 0);
+    g_list_free (successors);
+    g_assert (ufo_graph_get_num_edges (fixture->sequence) == 1);
+}
+
+static void
+test_get_labels (Fixture *fixture, gconstpointer data)
+{
+    g_assert (ufo_graph_get_edge_label (fixture->graph, fixture->root, fixture->target1) == FOO_LABEL);
+    g_assert (ufo_graph_get_edge_label (fixture->graph, fixture->root, fixture->target2) == BAR_LABEL);
+}
+
+static void
+test_expansion (Fixture *fixture, gconstpointer data)
+{
+    GList *successors;
+    UfoNode *node;
+    GList *path = NULL;
+
+    path = g_list_append (path, fixture->root);
+    path = g_list_append (path, fixture->target1);
+    path = g_list_append (path, fixture->target2);
+
+    ufo_graph_expand (fixture->sequence, path);
+    g_list_free (path);
+
+    successors = ufo_graph_get_successors (fixture->sequence, fixture->root);
+    g_assert (g_list_length (successors) == 2);
+
+    node = UFO_NODE (g_list_nth_data (successors, 0));
+    g_list_free (successors);
+
+    successors = ufo_graph_get_successors (fixture->sequence, node);
+    g_assert (g_list_length (successors) == 1);
+    node = UFO_NODE (g_list_nth_data (successors, 0));
+    g_list_free (successors);
+
+    g_assert (ufo_node_equal (node, fixture->target2));
+}
+
+static gboolean
+always_true (UfoNode *node, gpointer user_data)
+{
+    return TRUE;
+}
+
+static void
+test_get_nodes_filtered (Fixture *fixture, gconstpointer data)
+{
+    GList *nodes;
+
+    nodes = ufo_graph_get_nodes_filtered (fixture->sequence, always_true, NULL);
+    g_assert (g_list_length (nodes) == 3);
+    g_assert (g_list_find (nodes, fixture->root) != NULL);
+    g_assert (g_list_find (nodes, fixture->target1) != NULL);
+    g_assert (g_list_find (nodes, fixture->target2) != NULL);
+    g_list_free (nodes);
+}
+
+void
+test_add_graph (void)
+{
+    g_test_add ("/graph/connected",
+                Fixture, NULL,
+                fixture_setup, test_connected, fixture_teardown);
+
+    g_test_add ("/graph/nodes/number",
+                Fixture, NULL,
+                fixture_setup, test_get_num_nodes, fixture_teardown);
+
+    g_test_add ("/graph/nodes/roots",
+                Fixture, NULL,
+                fixture_setup, test_get_roots, fixture_teardown);
+
+    g_test_add ("/graph/nodes/successors",
+                Fixture, NULL,
+                fixture_setup, test_get_successors, fixture_teardown);
+
+    g_test_add ("/graph/nodes/predecessors",
+                Fixture, NULL,
+                fixture_setup, test_get_predecessors, fixture_teardown);
+
+    g_test_add ("/graph/nodes/filtered",
+                Fixture, NULL,
+                fixture_setup, test_get_nodes_filtered, fixture_teardown);
+
+    g_test_add ("/graph/edges/number",
+                Fixture, NULL,
+                fixture_setup, test_get_num_edges, fixture_teardown);
+
+    g_test_add ("/graph/edges/all",
+                Fixture, NULL,
+                fixture_setup, test_get_edges, fixture_teardown);
+
+    g_test_add ("/graph/edges/remove",
+                Fixture, NULL,
+                fixture_setup, test_remove_edge, fixture_teardown);
+
+    g_test_add ("/graph/labels",
+                Fixture, NULL,
+                fixture_setup, test_get_labels, fixture_teardown);
+
+    g_test_add ("/graph/expansion",
+                Fixture, NULL,
+                fixture_setup, test_expansion, fixture_teardown);
+}
diff --git a/tests/test-profiler.c b/tests/test-profiler.c
new file mode 100644
index 0000000..ce9b4ae
--- /dev/null
+++ b/tests/test-profiler.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <stdio.h>
+#include <string.h>
+#include <ufo.h>
+#include "test-suite.h"
+
+typedef struct {
+    UfoProfiler *profiler;
+} Fixture;
+
+
+static void
+fixture_setup (Fixture *fixture, gconstpointer data)
+{
+    fixture->profiler = ufo_profiler_new (UFO_PROFILER_LEVEL_OPENCL |
+                                          UFO_PROFILER_LEVEL_IO);
+    g_assert (UFO_IS_PROFILER (fixture->profiler));
+}
+
+static void
+fixture_teardown (Fixture *fixture, gconstpointer data)
+{
+    g_object_unref (fixture->profiler);
+}
+
+static void
+test_timer_elapsed (Fixture *fixture, gconstpointer data)
+{
+    gulong one_millisecond = G_USEC_PER_SEC / 1000;
+
+    ufo_profiler_start (fixture->profiler,
+                        UFO_PROFILER_TIMER_IO);
+
+    g_usleep (one_millisecond);
+
+    ufo_profiler_stop (fixture->profiler,
+                       UFO_PROFILER_TIMER_IO);
+
+    g_assert (ufo_profiler_elapsed (fixture->profiler,
+                                    UFO_PROFILER_TIMER_CPU) <= 0.0);
+
+    g_assert (ufo_profiler_elapsed (fixture->profiler,
+                                    UFO_PROFILER_TIMER_IO) >= 0.001);
+}
+
+
+void
+test_add_profiler (void)
+{
+    g_test_add ("/timer/elapsed",
+                Fixture,
+                NULL,
+                fixture_setup,
+                test_timer_elapsed,
+                fixture_teardown);
+}
diff --git a/tests/test-suite.c b/tests/test-suite.c
new file mode 100644
index 0000000..d16b214
--- /dev/null
+++ b/tests/test-suite.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib-object.h>
+#include "test-suite.h"
+
+static void
+ignore_log (const gchar     *domain,
+            GLogLevelFlags   flags,
+            const gchar     *message,
+            gpointer         data)
+{
+}
+
+int main(int argc, char *argv[])
+{
+    g_type_init();
+    g_test_init(&argc, &argv, NULL);
+    g_test_bug_base("http://ufo.kit.edu/ufo/ticket");
+
+    g_log_set_handler ("Ufo", G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG, ignore_log, NULL);
+    g_log_set_handler ("ocl", G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG, ignore_log, NULL);
+
+    test_add_config ();
+    test_add_graph ();
+    test_add_profiler ();
+
+    g_test_run();
+
+    return 0;
+}
diff --git a/tests/test-suite.h b/tests/test-suite.h
new file mode 100644
index 0000000..02bf8ff
--- /dev/null
+++ b/tests/test-suite.h
@@ -0,0 +1,8 @@
+#ifndef TEST_SUITE_H
+#define TEST_SUITE_H
+
+void test_add_config (void);
+void test_add_graph (void);
+void test_add_profiler (void);
+
+#endif
diff --git a/tests/test.json b/tests/test.json
new file mode 100644
index 0000000..daf9c2a
--- /dev/null
+++ b/tests/test.json
@@ -0,0 +1,24 @@
+{
+     "type" : "sequence",
+     "elements" : [
+         {
+             "type" : "filter",
+             "plugin" : "uca"
+         },
+         {
+             "type" : "filter",
+             "plugin" : "scale",
+             "properties" : {
+                 "scale" : 0.5
+             }
+         },
+         {
+             "type" : "filter",
+             "plugin" : "raw",
+             "properties" : {
+                 "prefix" : "foo"
+             }
+         }
+     ]
+}
+
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..48af150
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 2.8)
+
+add_executable(runjson runjson.c)
+add_executable(ufod ufod.c)
+
+target_link_libraries(runjson ufo ${UFOCORE_DEPS})
+target_link_libraries(ufod ufo ${UFOCORE_DEPS})
+
+install(TARGETS ufod runjson
+        RUNTIME DESTINATION bin)
+
+install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/clprof
+        DESTINATION bin)
diff --git a/tools/clprof b/tools/clprof
new file mode 100755
index 0000000..7426bc9
--- /dev/null
+++ b/tools/clprof
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+
+import sys
+import csv
+import logging
+import optparse
+import numpy as np
+
+class UncommentedFile:
+    def __init__(self, f, commentchar='#'):
+        self.f = f
+        self.commentchar = commentchar
+
+    def next(self):
+        line = self.f.next()
+        while line.startswith(self.commentchar):
+            line = self.f.next()
+        return line
+
+    def __iter__(self):
+        return self
+
+
+def _show(plt, kernel_name, data, color):
+    data = data.reshape((-1, 5))
+    data[:,1:] = data[:,1:] * 1e-6
+    row  = data[0,:]
+    p    = plt.plot([row[3], row[4]], [row[0], row[0]], '%s-' % color, label=kernel_name)
+
+    for row in data[1:,:]:
+        plt.plot([row[3], row[4]], [row[0], row[0]], '%s-' % color)
+
+    return p
+
+
+def _print_stats(kernel, data, queues):
+    data = data.reshape((-1, 5))
+    n_rows = float(data.shape[0])
+
+    data[:,1:] *= 1e-6
+
+    # Get performance numbers in milli seconds
+    submit_delay = data[:,2] - data[:,1]
+    execution_delay = data[:,3] - data[:,2]
+    execution = data[:,4] - data[:,3]
+
+    # Get queue distribution
+    dist = [(data[data[:,0] == q]).shape[0] / n_rows for q in queues]
+    sdist = ':'.join([('%.1f' % q) for q in dist])
+
+    print '%-20s % 12.4f % 12.4f % 12.4f % 12.4f %12s' % (kernel,
+            np.mean(submit_delay),
+            np.mean(execution_delay),
+            np.mean(execution),
+            np.sum(execution),
+            sdist)
+
+def parse_rows(reader):
+    kernels = {}
+    queues = {}
+
+    for row in reader:
+        name = row[0]
+        queue = row[1]
+
+        try:
+            queue_number = queues[queue]
+        except KeyError:
+            queue_number = len(queues)
+            queues[queue] = queue_number
+
+        a = np.array([queue_number] + [float(x) for x in row[2:]])
+
+        try:
+            kernels[name] = np.hstack((kernels[name], a))
+        except KeyError:
+            kernels[name] = a
+
+    return (kernels, queues)
+
+def process_file(reader, options):
+    kernels, queues = parse_rows(reader)
+
+    header = '%-20s %12s %12s %12s %12s %12s' % ('Kernel', 'Submit Delay',
+            'Exec Delay', 'Kernel Exec', 'Total Exec', 'Queue Dist')
+    print header
+    print '-'*len(header)
+
+    for key in kernels:
+        _print_stats(key, kernels[key], queues.values())
+
+    if hasattr(options, 'enable_plot') and options.enable_plot:
+        colors = ['b', 'r', 'g', 'm']
+
+        for c, key in enumerate(kernels):
+            p = _show(plt, key, kernels[key], colors[c])
+            plt.legend([p], [key], loc=1)
+
+        plt.show()
+
+if __name__ == '__main__':
+    parser = optparse.OptionParser(usage="Usage: %prog [options] FILE")
+
+    try:
+        import matplotlib.pyplot as plt
+
+        parser.add_option('-p', '--plot',
+                            action='store_true',
+                            dest='enable_plot',
+                            help='Plot the traces',
+                            default=False)
+    except ImportError:
+        pass
+
+    options, args = parser.parse_args()
+
+    if not args:
+        parser.print_help()
+        sys.exit(0)
+
+    try:
+        with open(args[0]) as f:
+            reader = csv.reader(UncommentedFile(f), delimiter=' ')
+            process_file(reader, options)
+    except IOError:
+        print "Error: Could not read `%s'" % args[0]
diff --git a/tools/deploy.sh b/tools/deploy.sh
new file mode 100755
index 0000000..0527ab4
--- /dev/null
+++ b/tools/deploy.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+#
+# Usage: deploy.sh PREFIX LIBSUFFIX
+
+ZEROMQ_BASE=zeromq-3.2.2
+ZEROMQ_TARBALL=$ZEROMQ_BASE.tar.gz
+ZEROMQ_URL=http://download.zeromq.org/$ZEROMQ_TARBALL
+UFO_URL=http://ufo.kit.edu/git
+
+ROOT=$(pwd)
+PREFIX=$1
+LIBSUFFIX=$2
+
+export LD_LIBRARY_PATH=$PREFIX/lib$LIBSUFFIX
+export PKG_CONFIG_PATH=$LD_LIBRARY_PATH/pkgconfig
+export GI_TYPELIB_PATH=$LD_LIBRARY_PATH/girepository-1.0
+
+function update() {
+    cd $ROOT/$1
+    git pull
+    make || exit 1
+}
+
+function build_package() {
+    pkg-config --cflags --libs libzmq
+    printf "\n** Fetching $1\n"
+    cd $ROOT
+
+    if [ -d "$ROOT/$1" ]; then
+        cd $ROOT/$1
+        git pull
+    else
+        git clone $UFO_URL/$1
+        cd $ROOT/$1
+    fi
+
+    printf "\n** Building $1\n"
+    cd $ROOT/$1
+    cmake . -DCMAKE_INSTALL_PREFIX=$PREFIX -DLIB_SUFFIX=$LIBSUFFIX || exit 1
+    make || exit 1
+    make install
+}
+
+function install_package() {
+    cd $ROOT/$1
+    make install
+}
+
+function build() {
+    if [ ! -f $ROOT/$ZEROMQ_TARBALL ]; then
+        printf "\n** Fetching ZeroMQ\n"
+        wget $ZEROMQ_URL || die "ERROR: Could not fetch ZeroMQ"
+    fi
+
+    printf "\n** Building ZeroMQ\n"
+    tar xfz $ZEROMQ_TARBALL
+    cd $ROOT/$ZEROMQ_BASE
+    ./configure --prefix=$PREFIX --libdir=$LD_LIBRARY_PATH
+    make -j 4
+    make install
+
+    build_package "ufo-core"
+    build_package "oclfft"
+    build_package "ufo-filters"
+
+    # Link the typelib because the girepository framework is not searching
+    # /usr/local on openSUSE systems.
+    if [[ $EUID -eq 0 ]]; then
+        TYPELIB_PATH=$(ls $GI_TYPELIB_PATH/Ufo-*.typelib)
+        TYPELIB=$(basename $TYPELIB_PATH)
+        ln -s $TYPELIB_PATH /usr/lib$LIBSUFFIX/girepository-1.0/$TYPELIB
+        ldconfig
+    fi
+}
+
+if [ "$PREFIX" == "" ]; then
+    PREFIX=$ROOT/usr
+    printf "** Prefix not set, installing into $PREFIX\n"
+fi
+
+if [ "$PREFIX" == "update" ]; then
+    update "ufo-core"
+    update "ufo-filters"
+elif [ "$PREFIX" == "install" ]; then
+    install_package "$ZEROMQ_BASE"
+    install_package "ufo-core"
+    install_package "oclfft"
+    install_package "ufo-filters"
+else
+    build
+fi
diff --git a/tools/leakcheck.sh b/tools/leakcheck.sh
new file mode 100755
index 0000000..bdaed95
--- /dev/null
+++ b/tools/leakcheck.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+OPTS="--tool=memcheck --leak-check=full --show-reachable=yes --leak-resolution=high --num-callers=20"
+
+G_SLICE=always-malloc G_DEBUG=gc-friendly,resident-modules valgrind $OPTS --log-file=vgdump $1
diff --git a/tools/mkfilter.py b/tools/mkfilter.py
new file mode 100755
index 0000000..5258f39
--- /dev/null
+++ b/tools/mkfilter.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+"""
+This file generates GObject file templates that a filter author can use, to
+implement their own nodes.
+"""
+
+import sys
+import re
+import string
+import textwrap
+import argparse
+import jinja2
+
+def type_list():
+    return ', '.join(['`' + f + '\'' for f in FILTER_TYPES])
+
+class FilternameAction(argparse.Action):
+    def __call__(self, parser, namespace, values, option_string=None):
+        if not re.match(r"^[A-Z][a-z]([A-Z][a-z0-9]*)*", values):
+            raise argparse.ArgumentTypeError('Name must be a camel-cased C identifier')
+        setattr(namespace, self.dest, values)
+
+
+class TypeAction(argparse.Action):
+    def __call__(self, parser, namespace, values, option_string=None):
+        lower = values.lower()
+        if lower not in ['gpu', 'cpu']:
+            raise argparse.ArgumentTypeError("Type must be either `cpu' or `gpu'")
+        setattr(namespace, self.dest, values)
+
+
+def generate_file(args, env, suffix='h'):
+    camelcased = args.name
+    hyphenated = args.name[0].lower() + args.name[1:]
+
+    for letter in string.ascii_uppercase:
+        hyphenated = hyphenated.replace(letter, "-" + letter.lower())
+
+    underscored = hyphenated.replace('-', '_')
+    uppercased = underscored.upper()
+
+    template = env.get_template('ufo-%s-task.%s.in' % (args.type, suffix))
+    res = template.render(camelcased=camelcased,
+                          uppercased=uppercased,
+                          hyphenated=hyphenated,
+                          underscored=underscored,
+                          args=args)
+
+    filename = "ufo-%s-task.%s" % (hyphenated, suffix)
+
+    with open(filename, 'w') as f:
+        f.writelines(res)
+        f.close()
+        print "Wrote %s" % filename
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description='Generate UfoNode skeletons')
+    parser.add_argument('-n', '--name', required=True, type=str,
+                        action=FilternameAction,
+                        help='Name of the new filter in CamelCase')
+    parser.add_argument('-t', '--type', type=str, default='cpu',
+                        action=TypeAction,
+                        help="Either `cpu' or `gpu' (cpu is default)")
+    parser.add_argument('-d', '--disable-comments',
+                        action='store_false',
+                        help='Do not insert comments into source files')
+
+    try:
+        args = parser.parse_args()
+    except argparse.ArgumentTypeError as err:
+        print err
+        sys.exit(1)
+
+    env = jinja2.Environment(loader=jinja2.PackageLoader('mkfilter', 'templates'))
+
+    generate_file(args, env, 'h')
+    generate_file(args, env, 'c')
+
+    message = "If you are about to write a UFO internal filter, you should copy \
+the generated files into core/filters and adapt the CMakeLists.txt file. You \
+should only add the filter sources to ${ufo_SRCS} if all build dependencies are \
+met for your particular plugin.  Good luck!"
+    
+    print ""
+    print textwrap.fill(message)
diff --git a/tools/runjson.c b/tools/runjson.c
new file mode 100644
index 0000000..e108437
--- /dev/null
+++ b/tools/runjson.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of runjson.
+ *
+ * runjson 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 3 of the License, or (at your option) any later version.
+ *
+ * runjson 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 runjson.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <ufo/ufo.h>
+
+static void
+handle_error (const gchar *prefix, GError *error, UfoGraph *graph)
+{
+    if (error) {
+        g_warning ("%s: %s", prefix, error->message);
+        g_error_free (error);
+        exit (EXIT_FAILURE);
+    }
+}
+
+static GList *
+string_array_to_list (gchar **array)
+{
+    GList *result = NULL;
+
+    if (array == NULL)
+        return NULL;
+
+    for (guint i = 0; array[i] != NULL; i++)
+        result = g_list_append (result, array[i]);
+
+    return result;
+}
+
+static void
+execute_json (const gchar *filename,
+              gchar **paths,
+              gchar **addresses)
+{
+    UfoConfig       *config;
+    UfoTaskGraph    *task_graph;
+    UfoScheduler    *scheduler;
+    UfoPluginManager *manager;
+    GList *path_list = NULL;
+    GList *address_list = NULL;
+    GError *error = NULL;
+
+    config = ufo_config_new ();
+
+    path_list = string_array_to_list (paths);
+    ufo_config_add_paths (config, path_list);
+    g_list_free (path_list);
+
+    manager = ufo_plugin_manager_new (config);
+
+    task_graph = UFO_TASK_GRAPH (ufo_task_graph_new ());
+    ufo_task_graph_read_from_file (task_graph, manager, filename, &error);
+    handle_error ("Reading JSON", error, UFO_GRAPH (task_graph));
+
+    address_list = string_array_to_list (addresses);
+    scheduler = ufo_scheduler_new (config, address_list);
+    g_list_free (address_list);
+
+    ufo_scheduler_run (scheduler, task_graph, &error);
+    handle_error ("Executing", error, UFO_GRAPH (task_graph));
+
+    g_object_unref (task_graph);
+    g_object_unref (scheduler);
+    g_object_unref (manager);
+    g_object_unref (config);
+}
+
+int main(int argc, char *argv[])
+{
+    GOptionContext *context;
+    GError *error = NULL;
+    gchar **paths = NULL;
+    gchar **addresses = NULL;
+    gboolean show_version = FALSE;
+
+    GOptionEntry entries[] = {
+        { "path", 'p', 0, G_OPTION_ARG_STRING_ARRAY, &paths,
+          "Path to node plugins or OpenCL kernels", NULL },
+        { "address", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &addresses,
+          "Address of remote server running `ufod'", NULL },
+        { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version,
+          "Show version information", NULL },
+        { NULL }
+    };
+
+    g_type_init();
+
+    context = g_option_context_new ("FILE");
+    g_option_context_add_main_entries (context, entries, NULL);
+
+    if (!g_option_context_parse (context, &argc, &argv, &error)) {
+        g_print ("Option parsing failed: %s\n", error->message);
+        return 1;
+    }
+
+    if (show_version) {
+        g_print ("runjson %s\n", UFO_VERSION);
+        exit (EXIT_SUCCESS);
+    }
+
+    if (argc < 2) {
+        gchar *help;
+
+        help = g_option_context_get_help (context, TRUE, NULL);
+        g_print ("%s", help);
+        g_free (help);
+        return 1;
+    }
+
+    execute_json (argv[argc-1], paths, addresses);
+
+    g_strfreev (paths);
+    g_strfreev (addresses);
+    g_option_context_free (context);
+
+    return 0;
+}
diff --git a/tools/templates/ufo-cpu-task.c.in b/tools/templates/ufo-cpu-task.c.in
new file mode 100644
index 0000000..1007dd5
--- /dev/null
+++ b/tools/templates/ufo-cpu-task.c.in
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ufo-{{hyphenated}}-task.h"
+
+/**
+ * SECTION:ufo-{{hyphenated}}-task
+ * @Short_description: Write TIFF files
+ * @Title: {{underscored}}
+ *
+ */
+
+struct _Ufo{{camelcased}}TaskPrivate {
+    gboolean foo;
+};
+
+static void ufo_task_interface_init (UfoTaskIface *iface);
+static void ufo_cpu_task_interface_init (UfoCpuTaskIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (Ufo{{camelcased}}Task, ufo_{{underscored}}_task, UFO_TYPE_TASK_NODE,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_TASK,
+                                                ufo_task_interface_init)
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_CPU_TASK,
+                                                ufo_cpu_task_interface_init))
+
+#define UFO_{{uppercased}}_TASK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_{{uppercased}}_TASK, Ufo{{camelcased}}TaskPrivate))
+
+enum {
+    PROP_0,
+    PROP_TEST,
+    N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+UfoNode *
+ufo_{{underscored}}_task_new (void)
+{
+    return UFO_NODE (g_object_new (UFO_TYPE_{{uppercased}}_TASK, NULL));
+}
+
+static void
+ufo_{{underscored}}_task_setup (UfoTask *task,
+                       UfoResources *resources,
+                       GError **error)
+{
+}
+
+static void
+ufo_{{underscored}}_task_get_requisition (UfoTask *task,
+                                 UfoBuffer **inputs,
+                                 UfoRequisition *requisition)
+{
+    requisition->n_dims = 0;
+}
+
+static void
+ufo_{{underscored}}_task_get_structure (UfoTask *task,
+                               guint *n_inputs,
+                               UfoInputParam **in_params,
+                               UfoTaskMode *mode)
+{
+    *mode = UFO_TASK_MODE_SINGLE;
+    *n_inputs = 1;
+    *in_params = g_new0 (UfoInputParam, 1);
+    (*in_params)[0].n_dims = 2;
+}
+
+static gboolean
+ufo_{{underscored}}_task_process (UfoCpuTask *task,
+                         UfoBuffer **inputs,
+                         UfoBuffer *output,
+                         UfoRequisition *requisition)
+{
+    return TRUE;
+}
+
+static void
+ufo_{{underscored}}_task_set_property (GObject *object,
+                              guint property_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+    Ufo{{camelcased}}TaskPrivate *priv = UFO_{{uppercased}}_TASK_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_TEST:
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_{{underscored}}_task_get_property (GObject *object,
+                              guint property_id,
+                              GValue *value,
+                              GParamSpec *pspec)
+{
+    Ufo{{camelcased}}TaskPrivate *priv = UFO_{{uppercased}}_TASK_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_TEST:
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_{{underscored}}_task_finalize (GObject *object)
+{
+    G_OBJECT_CLASS (ufo_{{underscored}}_task_parent_class)->finalize (object);
+}
+
+static void
+ufo_task_interface_init (UfoTaskIface *iface)
+{
+    iface->setup = ufo_{{underscored}}_task_setup;
+    iface->get_structure = ufo_{{underscored}}_task_get_structure;
+    iface->get_requisition = ufo_{{underscored}}_task_get_requisition;
+}
+
+static void
+ufo_cpu_task_interface_init (UfoCpuTaskIface *iface)
+{
+    iface->process = ufo_{{underscored}}_task_process;
+}
+
+static void
+ufo_{{underscored}}_task_class_init (Ufo{{camelcased}}TaskClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->set_property = ufo_{{underscored}}_task_set_property;
+    gobject_class->get_property = ufo_{{underscored}}_task_get_property;
+    gobject_class->finalize = ufo_{{underscored}}_task_finalize;
+
+    properties[PROP_TEST] =
+        g_param_spec_string ("test",
+            "Test property nick",
+            "Test property description blurb",
+            "",
+            G_PARAM_READWRITE);
+
+    for (guint i = PROP_0 + 1; i < N_PROPERTIES; i++)
+        g_object_class_install_property (gobject_class, i, properties[i]);
+
+    g_type_class_add_private (gobject_class, sizeof(Ufo{{camelcased}}TaskPrivate));
+}
+
+static void
+ufo_{{underscored}}_task_init(Ufo{{camelcased}}Task *self)
+{
+    self->priv = UFO_{{uppercased}}_TASK_GET_PRIVATE(self);
+}
diff --git a/tools/templates/ufo-cpu-task.h.in b/tools/templates/ufo-cpu-task.h.in
new file mode 100644
index 0000000..5102ca2
--- /dev/null
+++ b/tools/templates/ufo-cpu-task.h.in
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_{{uppercased}}_TASK_H
+#define __UFO_{{uppercased}}_TASK_H
+
+#include <ufo/ufo.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_{{uppercased}}_TASK             (ufo_{{underscored}}_task_get_type())
+#define UFO_{{uppercased}}_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_{{uppercased}}_TASK, Ufo{{camelcased}}Task))
+#define UFO_IS_{{uppercased}}_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_{{uppercased}}_TASK))
+#define UFO_{{uppercased}}_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_{{uppercased}}_TASK, Ufo{{camelcased}}TaskClass))
+#define UFO_IS_{{uppercased}}_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_{{uppercased}}_TASK))
+#define UFO_{{uppercased}}_TASK_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_{{uppercased}}_TASK, Ufo{{camelcased}}TaskClass))
+
+typedef struct _Ufo{{camelcased}}Task           Ufo{{camelcased}}Task;
+typedef struct _Ufo{{camelcased}}TaskClass      Ufo{{camelcased}}TaskClass;
+typedef struct _Ufo{{camelcased}}TaskPrivate    Ufo{{camelcased}}TaskPrivate;
+
+/**
+ * Ufo{{camelcased}}Task:
+ *
+ * [ADD DESCRIPTION HERE]. The contents of the #Ufo{{camelcased}}Task structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _Ufo{{camelcased}}Task {
+    /*< private >*/
+    UfoTaskNode parent_instance;
+
+    Ufo{{camelcased}}TaskPrivate *priv;
+};
+
+/**
+ * Ufo{{camelcased}}TaskClass:
+ *
+ * #Ufo{{camelcased}}Task class
+ */
+struct _Ufo{{camelcased}}TaskClass {
+    /*< private >*/
+    UfoTaskNodeClass parent_class;
+};
+
+UfoNode  *ufo_{{underscored}}_task_new       (void);
+GType     ufo_{{underscored}}_task_get_type  (void);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/templates/ufo-gpu-task.c.in b/tools/templates/ufo-gpu-task.c.in
new file mode 100644
index 0000000..c5593df
--- /dev/null
+++ b/tools/templates/ufo-gpu-task.c.in
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#include "ufo-{{hyphenated}}-task.h"
+
+/**
+ * SECTION:ufo-{{hyphenated}}-task
+ * @Short_description: Write TIFF files
+ * @Title: {{underscored}}
+ *
+ */
+
+struct _Ufo{{camelcased}}TaskPrivate {
+    gboolean foo;
+};
+
+static void ufo_task_interface_init (UfoTaskIface *iface);
+static void ufo_gpu_task_interface_init (UfoGpuTaskIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (Ufo{{camelcased}}Task, ufo_{{underscored}}_task, UFO_TYPE_TASK_NODE,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_TASK,
+                                                ufo_task_interface_init)
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_GPU_TASK,
+                                                ufo_gpu_task_interface_init))
+
+#define UFO_{{uppercased}}_TASK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_{{uppercased}}_TASK, Ufo{{camelcased}}TaskPrivate))
+
+enum {
+    PROP_0,
+    PROP_TEST,
+    N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+UfoNode *
+ufo_{{underscored}}_task_new (void)
+{
+    return UFO_NODE (g_object_new (UFO_TYPE_{{uppercased}}_TASK, NULL));
+}
+
+static void
+ufo_{{underscored}}_task_setup (UfoTask *task,
+                       UfoResources *resources,
+                       GError **error)
+{
+}
+
+static void
+ufo_{{underscored}}_task_get_requisition (UfoTask *task,
+                                 UfoBuffer **inputs,
+                                 UfoRequisition *requisition)
+{
+    requisition->n_dims = 0;
+}
+
+static void
+ufo_{{underscored}}_task_get_structure (UfoTask *task,
+                               guint *n_inputs,
+                               UfoInputParam **in_params,
+                               UfoTaskMode *mode)
+{
+    *mode = UFO_TASK_MODE_SINGLE;
+    *n_inputs = 1;
+    *in_params = g_new0 (UfoInputParam, 1);
+    (*in_params)[0].n_dims = 2;
+}
+
+static gboolean
+ufo_{{underscored}}_task_process (UfoGpuTask *task,
+                         UfoBuffer **inputs,
+                         UfoBuffer *output,
+                         UfoRequisition *requisition,
+                         UfoGpuNode *node)
+{
+    return TRUE;
+}
+
+static void
+ufo_{{underscored}}_task_set_property (GObject *object,
+                              guint property_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+    Ufo{{camelcased}}TaskPrivate *priv = UFO_{{uppercased}}_TASK_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_TEST:
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_{{underscored}}_task_get_property (GObject *object,
+                              guint property_id,
+                              GValue *value,
+                              GParamSpec *pspec)
+{
+    Ufo{{camelcased}}TaskPrivate *priv = UFO_{{uppercased}}_TASK_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_TEST:
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_{{underscored}}_task_finalize (GObject *object)
+{
+    G_OBJECT_CLASS (ufo_{{underscored}}_task_parent_class)->finalize (object);
+}
+
+static void
+ufo_task_interface_init (UfoTaskIface *iface)
+{
+    iface->setup = ufo_{{underscored}}_task_setup;
+    iface->get_structure = ufo_{{underscored}}_task_get_structure;
+    iface->get_requisition = ufo_{{underscored}}_task_get_requisition;
+}
+
+static void
+ufo_gpu_task_interface_init (UfoGpuTaskIface *iface)
+{
+    iface->process = ufo_{{underscored}}_task_process;
+}
+
+static void
+ufo_{{underscored}}_task_class_init (Ufo{{camelcased}}TaskClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->set_property = ufo_{{underscored}}_task_set_property;
+    gobject_class->get_property = ufo_{{underscored}}_task_get_property;
+    gobject_class->finalize = ufo_{{underscored}}_task_finalize;
+
+    properties[PROP_TEST] =
+        g_param_spec_string ("test",
+            "Test property nick",
+            "Test property description blurb",
+            "",
+            G_PARAM_READWRITE);
+
+    for (guint i = PROP_0 + 1; i < N_PROPERTIES; i++)
+        g_object_class_install_property (gobject_class, i, properties[i]);
+
+    g_type_class_add_private (gobject_class, sizeof(Ufo{{camelcased}}TaskPrivate));
+}
+
+static void
+ufo_{{underscored}}_task_init(Ufo{{camelcased}}Task *self)
+{
+    self->priv = UFO_{{uppercased}}_TASK_GET_PRIVATE(self);
+}
diff --git a/tools/templates/ufo-gpu-task.h.in b/tools/templates/ufo-gpu-task.h.in
new file mode 100644
index 0000000..5102ca2
--- /dev/null
+++ b/tools/templates/ufo-gpu-task.h.in
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_{{uppercased}}_TASK_H
+#define __UFO_{{uppercased}}_TASK_H
+
+#include <ufo/ufo.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_{{uppercased}}_TASK             (ufo_{{underscored}}_task_get_type())
+#define UFO_{{uppercased}}_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_{{uppercased}}_TASK, Ufo{{camelcased}}Task))
+#define UFO_IS_{{uppercased}}_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_{{uppercased}}_TASK))
+#define UFO_{{uppercased}}_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_{{uppercased}}_TASK, Ufo{{camelcased}}TaskClass))
+#define UFO_IS_{{uppercased}}_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_{{uppercased}}_TASK))
+#define UFO_{{uppercased}}_TASK_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_{{uppercased}}_TASK, Ufo{{camelcased}}TaskClass))
+
+typedef struct _Ufo{{camelcased}}Task           Ufo{{camelcased}}Task;
+typedef struct _Ufo{{camelcased}}TaskClass      Ufo{{camelcased}}TaskClass;
+typedef struct _Ufo{{camelcased}}TaskPrivate    Ufo{{camelcased}}TaskPrivate;
+
+/**
+ * Ufo{{camelcased}}Task:
+ *
+ * [ADD DESCRIPTION HERE]. The contents of the #Ufo{{camelcased}}Task structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _Ufo{{camelcased}}Task {
+    /*< private >*/
+    UfoTaskNode parent_instance;
+
+    Ufo{{camelcased}}TaskPrivate *priv;
+};
+
+/**
+ * Ufo{{camelcased}}TaskClass:
+ *
+ * #Ufo{{camelcased}}Task class
+ */
+struct _Ufo{{camelcased}}TaskClass {
+    /*< private >*/
+    UfoTaskNodeClass parent_class;
+};
+
+UfoNode  *ufo_{{underscored}}_task_new       (void);
+GType     ufo_{{underscored}}_task_get_type  (void);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/ufod.c b/tools/ufod.c
new file mode 100644
index 0000000..6caabae
--- /dev/null
+++ b/tools/ufod.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of ufod.
+ *
+ * ufod 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 3 of the License, or (at your option) any later version.
+ *
+ * ufod 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 ufod.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+#include <zmq.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ufo/ufo.h>
+
+typedef struct {
+    UfoConfig *config;
+    UfoPluginManager *manager;
+    UfoTaskGraph *task_graph;
+    UfoScheduler *scheduler;
+    gpointer socket;
+    UfoNode *input_task;
+    UfoNode *output_task;
+    UfoBuffer *input;
+} ServerPrivate;
+
+typedef struct {
+    gchar **paths;
+    gchar *addr;
+} Options;
+
+#define CHECK_ZMQ(r) if (r == -1) g_warning ("%s:%i: zmq_error: %s\n", __FILE__, __LINE__, zmq_strerror (errno));
+
+static gpointer run_scheduler (ServerPrivate *priv);
+
+static void
+ufo_msg_send (UfoMessage *msg, gpointer socket, gint flags)
+{
+    zmq_msg_t reply;
+
+    zmq_msg_init_size (&reply, sizeof (UfoMessage));
+    memcpy (zmq_msg_data (&reply), msg, sizeof (UfoMessage));
+    zmq_msg_send (&reply, socket, flags);
+    zmq_msg_close (&reply);
+}
+
+static void
+send_ack (gpointer socket)
+{
+    UfoMessage msg;
+    msg.type = UFO_MESSAGE_ACK;
+    ufo_msg_send (&msg, socket, 0);
+}
+
+static void
+handle_get_num_devices (ServerPrivate *priv)
+{
+    UfoMessage msg;
+    cl_context context;
+
+    context = ufo_scheduler_get_context (priv->scheduler);
+
+    UFO_RESOURCES_CHECK_CLERR (clGetContextInfo (context,
+                                                 CL_CONTEXT_NUM_DEVICES,
+                                                 sizeof (cl_uint),
+                                                 &msg.d.n_devices,
+                                                 NULL));
+
+    ufo_msg_send (&msg, priv->socket, 0);
+}
+
+static UfoNode *
+remove_dummy_if_present (UfoGraph *graph,
+                         UfoNode *first)
+{
+    UfoNode *real = first;
+
+    if (UFO_IS_DUMMY_TASK (first)) {
+        UfoNode *dummy;
+        GList *successors;
+
+        dummy = first;
+        successors = ufo_graph_get_successors (graph, dummy);
+        g_assert (g_list_length (successors) == 1);
+        real = UFO_NODE (successors->data);
+        g_list_free (successors);
+        ufo_graph_remove_edge (graph, dummy, real);
+    }
+
+    return real;
+}
+
+static void
+handle_json (ServerPrivate *priv)
+{
+    zmq_msg_t json_msg;
+    gsize size;
+    gchar *json;
+    GList *roots;
+    GList *leaves;
+    UfoNode *first;
+    UfoNode *last;
+    GError *error = NULL;
+
+    zmq_msg_init (&json_msg);
+    size = (gsize) zmq_msg_recv (&json_msg, priv->socket, 0);
+
+    json = g_malloc0 (size + 1);
+    memcpy (json, zmq_msg_data (&json_msg), size);
+    zmq_msg_close (&json_msg);
+
+    /* Setup local task graph */
+    priv->task_graph = UFO_TASK_GRAPH (ufo_task_graph_new ());
+    ufo_task_graph_read_from_data (priv->task_graph, priv->manager, json, &error);
+
+    if (error != NULL) {
+        g_printerr ("%s\n", error->message);
+        /* Send error to master */
+        return;
+    }
+
+    roots = ufo_graph_get_roots (UFO_GRAPH (priv->task_graph));
+    g_assert (g_list_length (roots) == 1);
+
+    leaves = ufo_graph_get_leaves (UFO_GRAPH (priv->task_graph));
+    g_assert (g_list_length (leaves) == 1);
+
+    first = UFO_NODE (g_list_nth_data (roots, 0));
+    last = UFO_NODE (g_list_nth_data (leaves, 0));
+
+    first = remove_dummy_if_present (UFO_GRAPH (priv->task_graph), first);
+
+    priv->input_task = ufo_input_task_new ();
+    priv->output_task = ufo_output_task_new (2);
+
+    ufo_graph_connect_nodes (UFO_GRAPH (priv->task_graph),
+                             priv->input_task, first,
+                             GINT_TO_POINTER (0));
+
+    ufo_graph_connect_nodes (UFO_GRAPH (priv->task_graph),
+                             last, priv->output_task,
+                             GINT_TO_POINTER (0));
+
+    g_thread_create ((GThreadFunc) run_scheduler, priv, FALSE, NULL);
+    g_free (json);
+    send_ack (priv->socket);
+}
+
+static void
+handle_setup (ServerPrivate *priv)
+{
+    g_message ("Setup requested");
+    send_ack (priv->socket);
+}
+
+static void
+handle_get_structure (ServerPrivate *priv)
+{
+    UfoMessage header;
+    UfoInputParam in_param;
+    zmq_msg_t data_msg;
+
+    g_message ("Structure requested");
+    header.type = UFO_MESSAGE_STRUCTURE;
+
+    /* TODO: do not hardcode these */
+    header.d.n_inputs = 1;
+    in_param.n_dims = 2;
+
+    zmq_msg_init_size (&data_msg, sizeof (UfoInputParam));
+    memcpy (zmq_msg_data (&data_msg), &in_param, sizeof (UfoInputParam));
+
+    ufo_msg_send (&header, priv->socket, ZMQ_SNDMORE);
+    zmq_msg_send (&data_msg, priv->socket, 0);
+    zmq_msg_close (&data_msg);
+}
+
+static void
+handle_send_inputs (ServerPrivate *priv)
+{
+    UfoRequisition *requisition;
+    zmq_msg_t requisition_msg;
+    zmq_msg_t data_msg;
+    gpointer context;
+
+    context = ufo_scheduler_get_context (priv->scheduler);
+
+    /* Receive buffer size */
+    zmq_msg_init (&requisition_msg);
+    zmq_msg_recv (&requisition_msg, priv->socket, 0);
+    g_assert (zmq_msg_size (&requisition_msg) >= sizeof (UfoRequisition));
+    requisition = zmq_msg_data (&requisition_msg);
+
+    if (priv->input == NULL) {
+        priv->input = ufo_buffer_new (requisition, context);
+    }
+    else {
+        if (ufo_buffer_cmp_dimensions (priv->input, requisition))
+            ufo_buffer_resize (priv->input, requisition);
+    }
+
+    zmq_msg_close (&requisition_msg);
+
+    /* Receive actual buffer */
+    zmq_msg_init (&data_msg);
+    zmq_msg_recv (&data_msg, priv->socket, 0);
+
+    memcpy (ufo_buffer_get_host_array (priv->input, NULL),
+            zmq_msg_data (&data_msg),
+            ufo_buffer_get_size (priv->input));
+
+    ufo_input_task_release_input_buffer (UFO_INPUT_TASK (priv->input_task), priv->input);
+    zmq_msg_close (&data_msg);
+
+    send_ack (priv->socket);
+}
+
+static void
+handle_get_requisition (ServerPrivate *priv)
+{
+    UfoRequisition requisition;
+    zmq_msg_t reply_msg;
+
+    /* We need to get the requisition from the last node */
+    g_message ("Requisition requested");
+    ufo_output_task_get_output_requisition (UFO_OUTPUT_TASK (priv->output_task),
+                                            &requisition);
+
+    zmq_msg_init_size (&reply_msg, sizeof (UfoRequisition));
+    memcpy (zmq_msg_data (&reply_msg), &requisition, sizeof (UfoRequisition));
+    zmq_msg_send (&reply_msg, priv->socket, 0);
+    zmq_msg_close (&reply_msg);
+}
+
+static
+void handle_get_result (ServerPrivate *priv)
+{
+    UfoBuffer *buffer;
+    zmq_msg_t reply_msg;
+    gsize size;
+
+    buffer = ufo_output_task_get_output_buffer (UFO_OUTPUT_TASK (priv->output_task));
+    size = ufo_buffer_get_size (buffer);
+
+    zmq_msg_init_size (&reply_msg, size);
+    memcpy (zmq_msg_data (&reply_msg), ufo_buffer_get_host_array (buffer, NULL), size);
+    zmq_msg_send (&reply_msg, priv->socket, 0);
+    zmq_msg_close (&reply_msg);
+    ufo_output_task_release_output_buffer (UFO_OUTPUT_TASK (priv->output_task), buffer);
+}
+
+static void
+unref_and_free (GObject **object)
+{
+    if (*object) {
+        g_object_unref (*object);
+        *object = NULL;
+    }
+}
+
+static
+void handle_cleanup (ServerPrivate *priv)
+{
+    /*
+     * We send the ACK early on, because we don't want to let the host wait for
+     * actually cleaning up (and waiting some time to unref the input task).
+     */
+    send_ack (priv->socket);
+
+    if (priv->input_task) {
+        ufo_input_task_stop (UFO_INPUT_TASK (priv->input_task));
+
+        ufo_input_task_release_input_buffer (UFO_INPUT_TASK (priv->input_task),
+                                             priv->input);
+
+        g_usleep (1.5 * G_USEC_PER_SEC);
+        unref_and_free ((GObject **) &priv->input_task);
+        unref_and_free ((GObject **) &priv->input);
+    }
+
+    unref_and_free ((GObject **) &priv->output_task);
+    unref_and_free ((GObject **) &priv->task_graph);
+}
+
+static gpointer
+run_scheduler (ServerPrivate *priv)
+{
+    g_message ("Start scheduler");
+    ufo_scheduler_run (priv->scheduler, priv->task_graph, NULL);
+
+    g_message ("Done");
+    g_object_unref (priv->scheduler);
+
+    priv->scheduler = ufo_scheduler_new (priv->config, NULL);
+    return NULL;
+}
+
+static Options *
+opts_parse (gint *argc, gchar ***argv)
+{
+    GOptionContext *context;
+    Options *opts;
+    gboolean show_version = FALSE;
+    GError *error = NULL;
+
+    opts = g_new0 (Options, 1);
+
+    GOptionEntry entries[] = {
+        { "listen", 'l', 0, G_OPTION_ARG_STRING, &opts->addr,
+          "Address to listen on (see http://api.zeromq.org/3-2:zmq-tcp)", NULL },
+        { "path", 'p', 0, G_OPTION_ARG_STRING_ARRAY, &opts->paths,
+          "Path to node plugins or OpenCL kernels", NULL },
+        { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version,
+          "Show version information", NULL },
+        { NULL }
+    };
+
+    context = g_option_context_new ("FILE");
+    g_option_context_add_main_entries (context, entries, NULL);
+
+    if (!g_option_context_parse (context, argc, argv, &error)) {
+        g_print ("Option parsing failed: %s\n", error->message);
+        g_free (opts);
+        return NULL;
+    }
+
+    if (show_version) {
+        g_print ("ufod %s\n", UFO_VERSION);
+        exit (EXIT_SUCCESS);
+    }
+
+    if (opts->addr == NULL)
+        opts->addr = g_strdup ("tcp://*:5555");
+
+    g_option_context_free (context);
+
+    return opts;
+}
+
+static UfoConfig *
+opts_new_config (Options *opts)
+{
+    UfoConfig *config;
+    GList *paths = NULL;
+
+    config = ufo_config_new ();
+
+    if (opts->paths != NULL) {
+        for (guint i = 0; opts->paths[i] != NULL; i++)
+            paths = g_list_append (paths, opts->paths[i]);
+
+        ufo_config_add_paths (config, paths);
+        g_list_free (paths);
+    }
+
+    return config;
+}
+
+static void
+opts_free (Options *opts)
+{
+    g_strfreev (opts->paths);
+    g_free (opts->addr);
+    g_free (opts);
+}
+
+int
+main (int argc, char * argv[])
+{
+    gpointer context;
+    Options *opts;
+    ServerPrivate priv;
+
+    g_type_init ();
+    g_thread_init (NULL);
+
+    if ((opts = opts_parse (&argc, &argv)) == NULL)
+        return 1;
+
+    memset (&priv, 0, sizeof (ServerPrivate));
+
+    priv.config = opts_new_config (opts);
+    priv.manager = ufo_plugin_manager_new (priv.config);
+    priv.scheduler = ufo_scheduler_new (priv.config, NULL);
+
+    /* start zmq service */
+    context = zmq_ctx_new ();
+    priv.socket = zmq_socket (context, ZMQ_REP);
+    zmq_bind (priv.socket, opts->addr);
+
+    g_print ("ufod %s - waiting for requests ...\n", UFO_VERSION);
+
+    while (1) {
+        zmq_msg_t request;
+
+        zmq_msg_init (&request);
+        zmq_msg_recv (&request, priv.socket, 0);
+
+        if (zmq_msg_size (&request) < sizeof (UfoMessage)) {
+            g_warning ("Message is smaller than expected\n");
+            send_ack (priv.socket);
+        }
+        else {
+            UfoMessage *msg;
+
+            msg = (UfoMessage *) zmq_msg_data (&request);
+
+            switch (msg->type) {
+                case UFO_MESSAGE_GET_NUM_DEVICES:
+                    handle_get_num_devices (&priv);
+                    break;
+                case UFO_MESSAGE_TASK_JSON:
+                    handle_json (&priv);
+                    break;
+                case UFO_MESSAGE_SETUP:
+                    handle_setup (&priv);
+                    break;
+                case UFO_MESSAGE_GET_STRUCTURE:
+                    handle_get_structure (&priv);
+                    break;
+                case UFO_MESSAGE_SEND_INPUTS:
+                    handle_send_inputs (&priv);
+                    break;
+                case UFO_MESSAGE_GET_REQUISITION:
+                    handle_get_requisition (&priv);
+                    break;
+                case UFO_MESSAGE_GET_RESULT:
+                    handle_get_result (&priv);
+                    break;
+                case UFO_MESSAGE_CLEANUP:
+                    handle_cleanup (&priv);
+                    break;
+                default:
+                    g_print ("unhandled case\n");
+            }
+        }
+
+        zmq_msg_close (&request);
+    }
+
+    g_object_unref (priv.task_graph);
+    g_object_unref (priv.config);
+    g_object_unref (priv.manager);
+    g_object_unref (priv.scheduler);
+
+    zmq_close (priv.socket);
+    zmq_ctx_destroy (context);
+
+    opts_free (opts);
+
+    return 0;
+}
diff --git a/ufo/CMakeLists.txt b/ufo/CMakeLists.txt
new file mode 100644
index 0000000..6dc6dee
--- /dev/null
+++ b/ufo/CMakeLists.txt
@@ -0,0 +1,271 @@
+cmake_minimum_required(VERSION 2.6)
+
+# --- Set sources -------------------------------------------------------------
+set(ufocore_SRCS
+    ufo-arch-graph.c
+    ufo-buffer.c
+    ufo-configurable.c
+    ufo-config.c
+    ufo-cpu-node.c
+    ufo-cpu-task-iface.c
+    ufo-dummy-task.c
+    ufo-gpu-node.c
+    ufo-gpu-task-iface.c
+    ufo-graph.c
+    ufo-group.c
+    ufo-input-task.c
+    ufo-node.c
+    ufo-output-task.c
+    ufo-plugin-manager.c
+    ufo-profiler.c
+    ufo-remote-node.c
+    ufo-remote-task.c
+    ufo-resources.c
+    ufo-scheduler.c
+    ufo-task-iface.c
+    ufo-task-graph.c
+    ufo-task-node.c
+    )
+
+set(ufocore_HDRS
+    ufo-arch-graph.h
+    ufo-buffer.h
+    ufo-configurable.h
+    ufo-config.h
+    ufo-cpu-node.h
+    ufo-cpu-task-iface.h
+    ufo-dummy-task.h
+    ufo-gpu-node.h
+    ufo-gpu-task-iface.h
+    ufo-graph.h
+    ufo-group.h
+    ufo-input-task.h
+    ufo-node.h
+    ufo-output-task.h
+    ufo-plugin-manager.h
+    ufo-profiler.h
+    ufo-remote-node.h
+    ufo-remote-task.h
+    ufo-resources.h
+    ufo-scheduler.h
+    ufo-task-iface.h
+    ufo-task-graph.h
+    ufo-task-node.h
+    )
+
+
+# --- Find packages and libraries ---------------------------------------------
+find_program(INTROSPECTION_SCANNER "g-ir-scanner")
+find_program(INTROSPECTION_COMPILER "g-ir-compiler")
+find_program(GLIB2_MKENUMS glib-mkenums REQUIRED)
+
+# --- Add enum generation targets ---------------------------------------------
+add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ufo-enums.h
+    COMMAND ${GLIB2_MKENUMS}
+    ARGS
+        --template ufo-enums.h.template
+        ${ufocore_HDRS} > ${CMAKE_CURRENT_BINARY_DIR}/ufo-enums.h
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+    DEPENDS ${ufocore_HDRS}
+            ${CMAKE_CURRENT_SOURCE_DIR}/ufo-enums.h.template
+)
+
+add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ufo-enums.c
+    COMMAND ${GLIB2_MKENUMS}
+    ARGS
+        --template ufo-enums.c.template
+        ${ufocore_HDRS} > ${CMAKE_CURRENT_BINARY_DIR}/ufo-enums.c
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+    DEPENDS ${ufocore_HDRS} ${CMAKE_CURRENT_BINARY_DIR}/ufo-enums.h
+            ${CMAKE_CURRENT_SOURCE_DIR}/ufo-enums.c.template
+)
+
+# --- Target ------------------------------------------------------------------
+get_property(LIB64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS)
+
+set(LIBDIR      "lib${LIB_SUFFIX}")
+set(INCLUDEDIR  "include/ufo")
+
+include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+add_definitions(-DUFO_COMPILATION)
+
+if(CMAKE_BUILD_TYPE MATCHES "Release")
+    add_definitions(-DG_DISABLE_ASSERT)
+endif()
+
+add_library(ufo SHARED
+            ${ufocore_SRCS}
+            ${ufocore_NODOC_SRCS}
+            ${CMAKE_CURRENT_BINARY_DIR}/ufo-enums.c)
+
+set_target_properties(ufo PROPERTIES
+    VERSION ${PACKAGE_VERSION}
+    SOVERSION ${UFO_SO_VERSION})
+
+target_link_libraries(ufo ${UFOCORE_DEPS})
+
+install(TARGETS ufo
+        ARCHIVE DESTINATION ${LIBDIR}
+        LIBRARY DESTINATION ${LIBDIR})
+
+install(FILES ${ufocore_HDRS}
+              ${CMAKE_CURRENT_SOURCE_DIR}/ufo.h
+              ${CMAKE_CURRENT_BINARY_DIR}/ufo-enums.h
+        DESTINATION ${INCLUDEDIR})
+
+
+# --- pkg-config --------------------------------------------------------------
+set(UFO_PKG_PREFIX      "${CMAKE_INSTALL_PREFIX}")
+set(UFO_PKG_EXEC_PREFIX "${UFO_PKG_PREFIX}/bin")
+set(UFO_PKG_INCLUDEDIR  "${UFO_PKG_PREFIX}/include")
+set(UFO_PKG_GIRDIR      "${UFO_PKG_PREFIX}/share/gir-1.0")
+set(UFO_PKG_LIBDIR      "${UFO_PKG_PREFIX}/${LIBDIR}")
+set(UFO_PKG_TYPELIBDIR  "${UFO_PKG_PREFIX}/${LIBDIR}/girepository-1.0")
+
+# FIXME: inside the ufo.pc.in we should set the lib names that we found out, not
+# hard coded values
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/ufo.pc.in"
+               "${CMAKE_CURRENT_BINARY_DIR}/ufo.pc" @ONLY IMMEDIATE)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ufo.pc
+        DESTINATION ${LIBDIR}/pkgconfig)
+
+
+# --- Introspection files -----------------------------------------------------
+if (INTROSPECTION_SCANNER AND INTROSPECTION_COMPILER)
+    option(WITH_GIR "Build introspection files" ON)
+
+    if (WITH_GIR)
+        set(GIR_PREFIX "Ufo-${UFO_GIR_VERSION}")
+        set(GIR_XML "${GIR_PREFIX}.gir")
+        set(GIR_TYPELIB "${GIR_PREFIX}.typelib")
+        set(_gir_input)
+
+        foreach(_src ${ufocore_SRCS} ${ufocore_HDRS})
+            list(APPEND _gir_input "${CMAKE_CURRENT_SOURCE_DIR}/${_src}")
+        endforeach()
+
+        add_custom_command(OUTPUT ${GIR_XML}
+            COMMAND ${INTROSPECTION_SCANNER}
+                    --namespace=Ufo
+                    --nsversion=${UFO_GIR_VERSION}
+                    --library=ufo
+                    --no-libtool
+                    --include=GObject-2.0
+                    --include=GModule-2.0
+                    --include=GLib-2.0
+                    -I${OPENCL_INCLUDE_DIRS}
+                    -I${CMAKE_CURRENT_SOURCE_DIR}/..
+                    -DUFO_COMPILATION
+                    --output ${GIR_XML}
+                    --warn-all
+                    ${_gir_input} > /dev/null
+            DEPENDS ${ufocore_SRCS}
+            WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+        add_custom_command(OUTPUT ${GIR_TYPELIB}
+            COMMAND ${INTROSPECTION_COMPILER}
+                    -o ${GIR_TYPELIB}
+                    ${GIR_XML}
+            DEPENDS ${GIR_XML}
+            WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+        add_custom_target(gir ALL DEPENDS ${GIR_XML} ${GIR_TYPELIB})
+        add_dependencies(gir ufo)
+
+        install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${GIR_XML}
+            DESTINATION share/gir-1.0)
+
+        install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${GIR_TYPELIB}
+            DESTINATION ${LIBDIR}/girepository-1.0)
+    endif()
+endif()
+
+
+# --- Generate config.h -------------------------------------------------------
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+
+
+# --- Build API reference -----------------------------------------------------
+pkg_check_modules(GTK_DOC gtk-doc)
+if(GTK_DOC_FOUND)
+    option(WITH_GTK_DOC "Build API reference" ON)
+    if (WITH_GTK_DOC)
+        set(docs_dir "${CMAKE_CURRENT_BINARY_DIR}/../docs")
+        set(docs_out "${docs_dir}/reference")
+        set(_xml_doc_input)
+
+        # Create xml entries for Ufo-docs.xml.in
+        foreach(_src ${ufocore_SRCS})
+            string(REPLACE ".c" ".xml" _xml_doc ${_src})
+            list(APPEND _xml_doc_input "<xi:include href=\"xml/${_xml_doc}\"/>")
+        endforeach()
+
+        string(REPLACE ";" "\n" _xml_doc_input ${_xml_doc_input})
+
+        get_directory_property(_current_include_dirs INCLUDE_DIRECTORIES)
+        set(GTK_DOC_CFLAGS)
+        foreach(_incl ${_current_include_dirs})
+            set(GTK_DOC_CFLAGS "-I${_incl} ${GTK_DOC_CFLAGS}")
+        endforeach()
+
+        set(GTK_DOC_LDFLAGS)
+        foreach(_lib ${UFOCORE_DEPS})
+            if (NOT ${_lib} MATCHES "^[/]")
+                set(GTK_DOC_LDFLAGS "-l${_lib} ${GTK_DOC_LDFLAGS}")
+            endif()
+        endforeach()
+
+        configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../docs/Ufo-docs.xml.in" "${docs_out}/Ufo-docs.xml")
+        configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../docs/scangobj.sh.in" "${docs_out}/scangobj.sh")
+
+        set(reference_files
+            "${docs_out}/index.html"
+            "${docs_out}/UfoScheduler.html"
+            "${docs_out}/style.css"
+            "${docs_out}/Ufo.devhelp2"
+            "${docs_out}/home.png"
+            "${docs_out}/left.png"
+            "${docs_out}/right.png"
+            "${docs_out}/up.png")
+
+        find_program(GTK_DOC_SCAN gtkdoc-scan REQUIRED)
+        find_program(GTK_DOC_SCANGOBJ gtkdoc-scangobj REQUIRED)
+        find_program(GTK_DOC_MKDB gtkdoc-mkdb REQUIRED)
+        find_program(GTK_DOC_MKHTML gtkdoc-mkhtml REQUIRED)
+
+        add_custom_command(OUTPUT ${docs_out}/Ufo-decl.txt
+            COMMAND ${GTK_DOC_SCAN}
+                    --module=Ufo
+                    --source-dir=${CMAKE_CURRENT_SOURCE_DIR}
+                    DEPENDS ${ufocore_SRCS}
+            WORKING_DIRECTORY ${docs_out})
+
+        add_custom_command(OUTPUT ${docs_out}/Ufo.args
+            COMMAND sh scangobj.sh
+            DEPENDS ${ufocore_SRCS} ${docs_out}/Ufo-decl.txt
+            WORKING_DIRECTORY ${docs_out})
+
+        add_custom_command(OUTPUT ${docs_out}/sgml.stamp
+            COMMAND ${GTK_DOC_MKDB}
+                    --module=Ufo
+                    --source-dir=${CMAKE_CURRENT_SOURCE_DIR}
+                    --sgml-mode
+                    --output-format=xml
+            DEPENDS ${docs_out}/Ufo.args ${docs_out}/Ufo-sections.txt ${ufocore_SRCS}
+            WORKING_DIRECTORY ${docs_out})
+
+        add_custom_command(OUTPUT ${docs_dir}/html.stamp
+            COMMAND ${GTK_DOC_MKHTML}
+                    Ufo
+                    ${docs_out}/Ufo-docs.xml
+            DEPENDS ${docs_out}/sgml.stamp
+            WORKING_DIRECTORY ${docs_out})
+
+        add_custom_target(reference ALL DEPENDS ${docs_dir}/html.stamp)
+
+        install(FILES ${reference_files} DESTINATION share/gtk-doc/html/Ufo)
+    endif()
+endif(GTK_DOC_FOUND)
diff --git a/ufo/Ufo.types b/ufo/Ufo.types
new file mode 100644
index 0000000..e69de29
diff --git a/ufo/config.h.in b/ufo/config.h.in
new file mode 100644
index 0000000..a3cada3
--- /dev/null
+++ b/ufo/config.h.in
@@ -0,0 +1,3 @@
+#cmakedefine WITH_PROFILING 1
+#define UFO_PLUGIN_DIR "${CMAKE_INSTALL_PREFIX}/${LIBDIR}/ufo"
+#define UFO_VERSION "${PACKAGE_VERSION}"
diff --git a/ufo/ufo-arch-graph.c b/ufo/ufo-arch-graph.c
new file mode 100644
index 0000000..9e44f1f
--- /dev/null
+++ b/ufo/ufo-arch-graph.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+#include <sys/sysinfo.h>
+#include <sched.h>
+#include <zmq.h>
+#include <ufo/ufo-arch-graph.h>
+#include <ufo/ufo-cpu-node.h>
+#include <ufo/ufo-gpu-node.h>
+#include <ufo/ufo-remote-node.h>
+
+/**
+ * SECTION:ufo-arch-graph
+ * @Short_description: Describe and hold #UfoGpuNode, #UfoCpuNode and
+ * #UfoRemoteNode
+ * @Title: UfoArchGraph
+ */
+
+G_DEFINE_TYPE (UfoArchGraph, ufo_arch_graph, UFO_TYPE_GRAPH)
+
+#define UFO_ARCH_GRAPH_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_ARCH_GRAPH, UfoArchGraphPrivate))
+
+struct _UfoArchGraphPrivate {
+    UfoResources *resources;
+    gpointer zmq_context;
+    gpointer ocl_context;
+    UfoNode **cpu_nodes;
+    UfoNode **gpu_nodes;
+    UfoNode **remote_nodes;
+    guint n_cpus;
+    guint n_gpus;
+    guint n_remotes;
+};
+
+/**
+ * ufo_arch_graph_new:
+ * @resources: An initialized #UfoResources object
+ * @remote_addresses: (element-type utf8): (allow-none): A #GList containing
+ * address strings.
+ *
+ * Returns: A new #UfoArchGraph.
+ */
+UfoGraph *
+ufo_arch_graph_new (UfoResources *resources,
+                    GList *remote_addresses)
+{
+    UfoArchGraph *graph;
+    UfoArchGraphPrivate *priv;
+    GList *cmd_queues;
+    cpu_set_t mask;
+
+    graph = UFO_ARCH_GRAPH (g_object_new (UFO_TYPE_ARCH_GRAPH, NULL));
+    priv = graph->priv;
+
+    g_object_ref (resources);
+    priv->resources = resources;
+    priv->ocl_context = ufo_resources_get_context (resources);
+
+    /* Create CPU nodes */
+    priv->n_cpus = (guint) get_nprocs ();
+    priv->cpu_nodes = g_new0 (UfoNode *, priv->n_cpus);
+
+    for (guint i = 0; i < priv->n_cpus; i++) {
+        CPU_ZERO (&mask);
+        CPU_SET (i, &mask);
+        priv->cpu_nodes[i] = ufo_cpu_node_new (&mask);
+    }
+
+    /* Create GPU nodes, each one is associated with its own command queue. */
+    cmd_queues = ufo_resources_get_cmd_queues (resources);
+    priv->n_gpus = g_list_length (cmd_queues);
+    priv->gpu_nodes = g_new0 (UfoNode *, priv->n_gpus);
+
+    for (guint i = 0; i < priv->n_gpus; i++) {
+        priv->gpu_nodes[i] = ufo_gpu_node_new (g_list_nth_data (cmd_queues, i));
+    }
+
+    /* Create remote nodes */
+    priv->n_remotes = g_list_length (remote_addresses);
+
+    if (priv->n_remotes > 0) {
+        priv->zmq_context = zmq_ctx_new ();
+        priv->remote_nodes = g_new0 (UfoNode *, priv->n_remotes);
+
+        for (guint i = 0; i < priv->n_remotes; i++) {
+            priv->remote_nodes[i] = ufo_remote_node_new (priv->zmq_context,
+                                                         (gchar *) g_list_nth_data (remote_addresses, i));
+        }
+    }
+
+    /*
+     * Connect all CPUs to all GPUs. In the future this is the place for a
+     * NUMA-specific mapping.
+     */
+    for (guint i = 0; i < priv->n_cpus; i++) {
+        for (guint j = 0; j < priv->n_gpus; j++) {
+            ufo_graph_connect_nodes (UFO_GRAPH (graph),
+                                     priv->cpu_nodes[i],
+                                     priv->gpu_nodes[j],
+                                     NULL);
+        }
+
+        for (guint j = 0; j < priv->n_remotes; j++) {
+            ufo_graph_connect_nodes (UFO_GRAPH (graph),
+                                     priv->cpu_nodes[i],
+                                     priv->remote_nodes[j],
+                                     NULL);
+        }
+    }
+
+    g_list_free (cmd_queues);
+    return UFO_GRAPH (graph);
+}
+
+/**
+ * ufo_arch_graph_get_num_cpus:
+ * @graph: A #UfoArchGraph object
+ *
+ * Returns: Number of CPU nodes in @graph.
+ */
+guint
+ufo_arch_graph_get_num_cpus (UfoArchGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_ARCH_GRAPH (graph), 0);
+    return graph->priv->n_cpus;
+}
+
+/**
+ * ufo_arch_graph_get_num_gpus:
+ * @graph: A #UfoArchGraph object
+ *
+ * Returns: Number of GPU nodes in @graph.
+ */
+guint
+ufo_arch_graph_get_num_gpus (UfoArchGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_ARCH_GRAPH (graph), 0);
+    return graph->priv->n_gpus;
+}
+
+/**
+ * ufo_arch_graph_get_num_remotes:
+ * @graph: A #UfoArchGraph object
+ *
+ * Returns: Number of remote nodes in @graph.
+ */
+guint
+ufo_arch_graph_get_num_remotes (UfoArchGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_ARCH_GRAPH (graph), 0);
+    return graph->priv->n_remotes;
+}
+
+static gboolean
+is_gpu_node (UfoNode *node, gpointer user_data)
+{
+    return UFO_IS_GPU_NODE (node);
+}
+
+/**
+ * ufo_arch_graph_get_gpu_nodes:
+ * @graph: A #UfoArchGraph
+ *
+ * Returns: (element-type UfoGpuNode) (transfer container): A list of
+ * #UfoGpuNode elements in @graph.
+ */
+GList *
+ufo_arch_graph_get_gpu_nodes (UfoArchGraph *graph)
+{
+    return ufo_graph_get_nodes_filtered (UFO_GRAPH (graph),
+                                         is_gpu_node,
+                                         NULL);
+}
+
+static gboolean
+is_remote_node (UfoNode *node, gpointer user_data)
+{
+    return UFO_IS_REMOTE_NODE (node);
+}
+
+/**
+ * ufo_arch_graph_get_remote_nodes:
+ * @graph: A #UfoArchGraph
+ *
+ * Returns: (element-type UfoGpuNode) (transfer container): A list of
+ * #UfoRemoteNode elements in @graph.
+ */
+GList *
+ufo_arch_graph_get_remote_nodes (UfoArchGraph *graph)
+{
+    return ufo_graph_get_nodes_filtered (UFO_GRAPH (graph),
+                                         is_remote_node,
+                                         NULL);
+}
+
+static void
+unref_node_array (UfoNode **array,
+                  guint n_nodes)
+{
+    for (guint i = 0; i < n_nodes; i++) {
+        if (array[i] != NULL) {
+            g_object_unref (array[i]);
+            array[i] = NULL;
+        }
+    }
+}
+
+static void
+ufo_arch_graph_dispose (GObject *object)
+{
+    UfoArchGraphPrivate *priv;
+
+    priv = UFO_ARCH_GRAPH_GET_PRIVATE (object);
+
+    unref_node_array (priv->cpu_nodes, priv->n_cpus);
+    unref_node_array (priv->gpu_nodes, priv->n_gpus);
+    unref_node_array (priv->remote_nodes, priv->n_remotes);
+
+    if (priv->resources != NULL) {
+        g_object_unref (priv->resources);
+        priv->resources = NULL;
+    }
+
+    G_OBJECT_CLASS (ufo_arch_graph_parent_class)->dispose (object);
+}
+
+static void
+ufo_arch_graph_finalize (GObject *object)
+{
+    UfoArchGraphPrivate *priv;
+
+    priv = UFO_ARCH_GRAPH_GET_PRIVATE (object);
+
+    if (priv->zmq_context != NULL) {
+        g_debug ("Destroy zmq_context=%p", priv->zmq_context);
+        zmq_ctx_destroy (priv->zmq_context);
+        priv->zmq_context = NULL;
+    }
+
+    g_free (priv->cpu_nodes);
+    g_free (priv->gpu_nodes);
+    g_free (priv->remote_nodes);
+
+    priv->cpu_nodes = NULL;
+    priv->gpu_nodes = NULL;
+    priv->remote_nodes = NULL;
+
+    G_OBJECT_CLASS (ufo_arch_graph_parent_class)->finalize (object);
+}
+
+static void
+ufo_arch_graph_class_init (UfoArchGraphClass *klass)
+{
+    GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+    oclass->dispose = ufo_arch_graph_dispose;
+    oclass->finalize = ufo_arch_graph_finalize;
+
+    g_type_class_add_private(klass, sizeof(UfoArchGraphPrivate));
+}
+
+static void
+ufo_arch_graph_init (UfoArchGraph *self)
+{
+    UfoArchGraphPrivate *priv;
+    self->priv = priv = UFO_ARCH_GRAPH_GET_PRIVATE (self);
+
+    priv->cpu_nodes = NULL;
+    priv->gpu_nodes = NULL;
+    priv->remote_nodes = NULL;
+
+    ufo_graph_register_node_type (UFO_GRAPH (self), UFO_TYPE_CPU_NODE);
+    ufo_graph_register_node_type (UFO_GRAPH (self), UFO_TYPE_GPU_NODE);
+    ufo_graph_register_node_type (UFO_GRAPH (self), UFO_TYPE_REMOTE_NODE);
+}
diff --git a/ufo/ufo-arch-graph.h b/ufo/ufo-arch-graph.h
new file mode 100644
index 0000000..9c81e19
--- /dev/null
+++ b/ufo/ufo-arch-graph.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_ARCH_GRAPH_H
+#define __UFO_ARCH_GRAPH_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-graph.h>
+#include <ufo/ufo-resources.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_ARCH_GRAPH             (ufo_arch_graph_get_type())
+#define UFO_ARCH_GRAPH(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_ARCH_GRAPH, UfoArchGraph))
+#define UFO_IS_ARCH_GRAPH(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_ARCH_GRAPH))
+#define UFO_ARCH_GRAPH_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_ARCH_GRAPH, UfoArchGraphClass))
+#define UFO_IS_ARCH_GRAPH_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_ARCH_GRAPH))
+#define UFO_ARCH_GRAPH_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_ARCH_GRAPH, UfoArchGraphClass))
+
+typedef struct _UfoArchGraph           UfoArchGraph;
+typedef struct _UfoArchGraphClass      UfoArchGraphClass;
+typedef struct _UfoArchGraphPrivate    UfoArchGraphPrivate;
+
+/**
+ * UfoArchGraph:
+ *
+ * Graph structure that describes the relation between hardware nodes. The
+ * contents of the #UfoArchGraph structure are private and should only be
+ * accessed via the provided API.
+ */
+struct _UfoArchGraph {
+    /*< private >*/
+    UfoGraph parent_instance;
+
+    UfoArchGraphPrivate *priv;
+};
+
+/**
+ * UfoArchGraphClass:
+ *
+ * #UfoArchGraph class
+ */
+struct _UfoArchGraphClass {
+    /*< private >*/
+    UfoGraphClass parent_class;
+};
+
+UfoGraph    *ufo_arch_graph_new              (UfoResources   *resources,
+                                              GList          *remote_addresses);
+guint        ufo_arch_graph_get_num_cpus     (UfoArchGraph   *graph);
+guint        ufo_arch_graph_get_num_gpus     (UfoArchGraph   *graph);
+guint        ufo_arch_graph_get_num_remotes  (UfoArchGraph   *graph);
+GList       *ufo_arch_graph_get_gpu_nodes    (UfoArchGraph   *graph);
+GList       *ufo_arch_graph_get_remote_nodes (UfoArchGraph   *graph);
+GType        ufo_arch_graph_get_type         (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-buffer.c b/ufo/ufo-buffer.c
new file mode 100644
index 0000000..cf97967
--- /dev/null
+++ b/ufo/ufo-buffer.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#include <ufo/ufo-buffer.h>
+#include <ufo/ufo-resources.h>
+
+/**
+ * SECTION:ufo-buffer
+ * @Short_description: Represents n-dimensional data
+ * @Title: UfoBuffer
+ */
+
+G_DEFINE_TYPE(UfoBuffer, ufo_buffer, G_TYPE_OBJECT)
+
+#define UFO_BUFFER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_BUFFER, UfoBufferPrivate))
+
+enum {
+    PROP_0,
+    PROP_ID,
+    PROP_STRUCTURE,
+    N_PROPERTIES
+};
+
+typedef struct {
+    guint num_dims;
+    gfloat *data;
+    gsize dim_size[UFO_BUFFER_MAX_NDIMS];
+} nd_array;
+
+struct _UfoBufferPrivate {
+    nd_array            host_array;
+    cl_mem              device_array;
+    cl_context          context;
+    cl_command_queue    last_queue;
+    gsize               size;   /**< size of buffer in bytes */
+    UfoMemLocation      location;
+    GTimer             *timer;
+};
+
+static void
+alloc_mem (UfoBufferPrivate *priv,
+           UfoRequisition *requisition)
+{
+    cl_int err;
+
+    if (priv->host_array.data != NULL)
+        g_free (priv->host_array.data);
+
+    if (priv->device_array != NULL)
+        clReleaseMemObject (priv->device_array);
+
+    priv->size = sizeof(gfloat);
+    priv->host_array.num_dims = requisition->n_dims;
+
+    for (guint i = 0; i < requisition->n_dims; i++) {
+        priv->host_array.dim_size[i] = requisition->dims[i];
+        priv->size *= requisition->dims[i];
+    }
+
+    priv->host_array.data = g_malloc0 (priv->size);
+    priv->device_array = clCreateBuffer (priv->context,
+                                         CL_MEM_READ_WRITE, /* XXX: we _should_ evaluate USE_HOST_PTR */
+                                         priv->size,
+                                         NULL,
+                                         &err);
+    UFO_RESOURCES_CHECK_CLERR (err);
+    priv->location = UFO_LOCATION_HOST;
+}
+
+/**
+ * ufo_buffer_new:
+ * @requisition: (in): size requisition
+ * @context: (in): cl_context to use for creating the device array
+ *
+ * Create a new #UfoBuffer.
+ *
+ * Return value: A new #UfoBuffer with the given dimensions.
+ */
+UfoBuffer *
+ufo_buffer_new (UfoRequisition *requisition,
+                gpointer context)
+{
+    UfoBuffer *buffer;
+
+    g_return_val_if_fail ((requisition->n_dims <= UFO_BUFFER_MAX_NDIMS), NULL);
+    buffer = UFO_BUFFER (g_object_new (UFO_TYPE_BUFFER, NULL));
+    buffer->priv->context = context;
+
+    alloc_mem (buffer->priv, requisition);
+    return buffer;
+}
+
+/**
+ * ufo_buffer_get_size:
+ * @buffer: A #UfoBuffer
+ *
+ * Get the number of bytes of raw data that is managed by the @buffer.
+ *
+ * Returns: The size of @buffer's data.
+ */
+gsize
+ufo_buffer_get_size (UfoBuffer *buffer)
+{
+    g_return_val_if_fail (UFO_IS_BUFFER (buffer), 0);
+    return buffer->priv->size;
+}
+
+static void
+copy_host_to_host (UfoBufferPrivate *src_priv,
+                   UfoBufferPrivate *dst_priv)
+{
+    g_memmove (dst_priv->host_array.data,
+               src_priv->host_array.data,
+               src_priv->size);
+}
+
+static void
+copy_device_to_device (UfoBufferPrivate *src_priv,
+                       UfoBufferPrivate *dst_priv)
+{
+    cl_event event;
+    cl_int errcode;
+    cl_command_queue cmd_queue;
+
+    cmd_queue = src_priv->last_queue != NULL ? src_priv->last_queue : dst_priv->last_queue;
+    g_assert (cmd_queue != NULL);
+
+    errcode = clEnqueueCopyBuffer (cmd_queue,
+                                   src_priv->device_array,
+                                   dst_priv->device_array,
+                                   0, 0,                      /* offsets */
+                                   src_priv->size,
+                                   0, NULL, &event);
+
+    UFO_RESOURCES_CHECK_CLERR (errcode);
+    UFO_RESOURCES_CHECK_CLERR (clWaitForEvents (1, &event));
+    UFO_RESOURCES_CHECK_CLERR (clReleaseEvent (event));
+}
+
+static void
+ufo_buffer_to_host (UfoBuffer *buffer, gpointer cmd_queue)
+{
+    UfoBufferPrivate *priv;
+    cl_int cl_err;
+    cl_command_queue queue;
+
+    g_return_if_fail (UFO_IS_BUFFER (buffer));
+    priv = buffer->priv;
+
+    queue = cmd_queue == NULL ? priv->last_queue : cmd_queue;
+    priv->last_queue = cmd_queue;
+
+    if (priv->location == UFO_LOCATION_HOST)
+        return;
+
+    cl_err = clEnqueueReadBuffer (queue,
+                                  priv->device_array,
+                                  CL_TRUE,
+                                  0, priv->size,
+                                  priv->host_array.data,
+                                  0, NULL, NULL);
+
+    priv->location = UFO_LOCATION_HOST;
+    UFO_RESOURCES_CHECK_CLERR (cl_err);
+}
+
+static void
+ufo_buffer_to_device (UfoBuffer *buffer, gpointer cmd_queue)
+{
+    UfoBufferPrivate *priv;
+    cl_int cl_err;
+    cl_command_queue queue;
+
+    g_return_if_fail (UFO_IS_BUFFER (buffer));
+    priv = buffer->priv;
+
+    queue = cmd_queue == NULL ? priv->last_queue : cmd_queue;
+    priv->last_queue = cmd_queue;
+    g_assert (cmd_queue);
+
+    if (priv->location == UFO_LOCATION_DEVICE)
+        return;
+
+    cl_err = clEnqueueWriteBuffer ((cl_command_queue) queue,
+                                   priv->device_array,
+                                   CL_TRUE,
+                                   0, priv->size,
+                                   priv->host_array.data,
+                                   0, NULL, NULL);
+
+    priv->location = UFO_LOCATION_DEVICE;
+    UFO_RESOURCES_CHECK_CLERR (cl_err);
+}
+
+/**
+ * ufo_buffer_copy:
+ * @src: Source #UfoBuffer
+ * @dst: Destination #UfoBuffer
+ *
+ * Copy contents of @src to @dst. The final memory location is determined by the
+ * destination buffer.
+ */
+void
+ufo_buffer_copy (UfoBuffer *src, UfoBuffer *dst)
+{
+    UfoBufferPrivate *spriv;
+    UfoBufferPrivate *dpriv;
+
+    g_return_if_fail (UFO_IS_BUFFER (src) && UFO_IS_BUFFER (dst));
+    g_return_if_fail (src->priv->size == dst->priv->size);
+
+    spriv = src->priv;
+    dpriv = dst->priv;
+
+    if (spriv->location == dpriv->location) {
+        switch (spriv->location) {
+            case UFO_LOCATION_HOST:
+                copy_host_to_host (spriv, dpriv);
+                break;
+            case UFO_LOCATION_DEVICE:
+                copy_device_to_device (spriv, dpriv);
+                break;
+            default:
+                g_warning ("oops, we should not copy invalid data");
+        }
+    }
+    else {
+        cl_command_queue cmd_queue;
+
+        cmd_queue = spriv->last_queue != NULL ? spriv->last_queue : dpriv->last_queue;
+
+        if (cmd_queue == NULL || dpriv->location == UFO_LOCATION_HOST) {
+            ufo_buffer_to_host (src, cmd_queue);
+            copy_host_to_host (spriv, dpriv);
+        }
+        else {
+            ufo_buffer_to_device (src, cmd_queue);
+            copy_device_to_device (spriv, dpriv);
+        }
+    }
+}
+
+/**
+ * ufo_buffer_dup:
+ * @buffer: A #UfoBuffer
+ *
+ * Create a new buffer with the same requisition as @buffer. Note, that this is
+ * not a copy of @buffer!
+ *
+ * Returns: (transfer full): A #UfoBuffer with the same size as @buffer.
+ */
+UfoBuffer *
+ufo_buffer_dup (UfoBuffer *buffer)
+{
+    UfoBuffer *copy;
+    UfoRequisition requisition;
+
+    ufo_buffer_get_requisition (buffer, &requisition);
+    copy = ufo_buffer_new (&requisition, buffer->priv->context);
+    return copy;
+}
+
+/**
+ * ufo_buffer_resize:
+ * @buffer: A #UfoBuffer
+ * @requisition: A #UfoRequisition structure
+ *
+ * Resize an existing buffer. If the new requisition has the same size as
+ * before, resizing is a no-op.
+ *
+ * Since: 0.2
+ */
+void
+ufo_buffer_resize (UfoBuffer *buffer,
+                   UfoRequisition *requisition)
+{
+    UfoBufferPrivate *priv;
+
+    g_return_if_fail (UFO_IS_BUFFER (buffer));
+
+    priv = UFO_BUFFER_GET_PRIVATE (buffer);
+
+    if (priv->host_array.data != NULL) {
+        g_free (priv->host_array.data);
+        priv->host_array.data = NULL;
+    }
+
+    if (priv->device_array != NULL) {
+        UFO_RESOURCES_CHECK_CLERR (clReleaseMemObject (priv->device_array));
+        priv->device_array = NULL;
+    }
+
+    alloc_mem (priv, requisition);
+}
+
+/**
+ * ufo_buffer_cmp_dimensions:
+ * @buffer: A #UfoBuffer
+ * @requisition: #UfoRequisition
+ *
+ * Compare the size of @buffer with a given @requisition.
+ *
+ * Returns: value < 0, 0 or > 0 if requisition is smaller, equal or larger.
+ */
+gint
+ufo_buffer_cmp_dimensions (UfoBuffer *buffer,
+                           UfoRequisition *requisition)
+{
+    gint result;
+    g_return_val_if_fail (UFO_IS_BUFFER(buffer), FALSE);
+
+    result = 0;
+
+    for (guint i = 0; i < buffer->priv->host_array.num_dims; i++) {
+        gint req_dim = (gint) requisition->dims[i];
+        gint host_dim = (gint) buffer->priv->host_array.dim_size[i];
+        result += req_dim - host_dim;
+    }
+
+    return result;
+}
+
+/**
+ * ufo_buffer_get_requisition:
+ * @buffer: A #UfoBuffer
+ * @requisition: (out): A location to store the requisition of @buffer
+ *
+ * Return the size of @buffer.
+ */
+void
+ufo_buffer_get_requisition (UfoBuffer *buffer,
+                            UfoRequisition *requisition)
+{
+    UfoBufferPrivate *priv;
+
+    g_return_if_fail (UFO_IS_BUFFER (buffer) && (requisition != NULL));
+    priv = buffer->priv;
+    requisition->n_dims = priv->host_array.num_dims;
+
+    for (guint i = 0; i < priv->host_array.num_dims; i++)
+        requisition->dims[i] = priv->host_array.dim_size[i];
+}
+
+/**
+ * ufo_buffer_get_host_array:
+ * @buffer: A #UfoBuffer.
+ * @cmd_queue: (allow-none): A cl_command_queue object or %NULL.
+ *
+ * Returns a flat C-array containing the raw float data.
+ *
+ * Returns: Float array.
+ */
+gfloat *
+ufo_buffer_get_host_array (UfoBuffer *buffer, gpointer cmd_queue)
+{
+    g_return_val_if_fail (UFO_IS_BUFFER (buffer), NULL);
+    ufo_buffer_to_host (buffer, cmd_queue);
+    return buffer->priv->host_array.data;
+}
+
+/**
+ * ufo_buffer_get_device_array:
+ * @buffer: A #UfoBuffer.
+ * @cmd_queue: (allow-none): A cl_command_queue object or %NULL.
+ *
+ * Return the current cl_mem object of @buffer. If the data is not yet in device
+ * memory, it is transfered via @cmd_queue to the object. If @cmd_queue is %NULL
+ * @cmd_queue, the last used command queue is used.
+ *
+ * Returns: (transfer none): A cl_mem object associated with @buffer.
+ */
+gpointer
+ufo_buffer_get_device_array (UfoBuffer *buffer, gpointer cmd_queue)
+{
+    g_return_val_if_fail (UFO_IS_BUFFER (buffer), NULL);
+    ufo_buffer_to_device (buffer, cmd_queue);
+    return buffer->priv->device_array;
+}
+
+/**
+ * ufo_buffer_discard_location:
+ * @buffer: A #UfoBuffer
+ * @location: Location to discard
+ *
+ * Discard @location and use "other" location without copying to it first.
+ */
+void
+ufo_buffer_discard_location (UfoBuffer *buffer,
+                             UfoMemLocation location)
+{
+    g_return_if_fail (UFO_IS_BUFFER (buffer));
+    buffer->priv->location = location == UFO_LOCATION_HOST ? UFO_LOCATION_DEVICE : UFO_LOCATION_HOST;
+}
+
+/**
+ * ufo_buffer_convert:
+ * @buffer: A #UfoBuffer
+ * @depth: Source bit depth of host data
+ *
+ * Convert host data according to its @depth to the internal 32-bit floating
+ * point representation.
+ */
+void
+ufo_buffer_convert (UfoBuffer *buffer,
+                    UfoBufferDepth depth)
+{
+    UfoBufferPrivate *priv;
+    gint n_pixels;
+    gfloat *dst;
+
+    g_return_if_fail (UFO_IS_BUFFER (buffer));
+    priv = buffer->priv;
+    n_pixels = (gint) (priv->size / 4);
+    dst = priv->host_array.data;
+
+    /* To save a memory allocation and several copies, we process data from back
+     * to front. This is possible if src bit depth is at most half as wide as
+     * the 32-bit target buffer. The processor cache should not be a
+     * problem. */
+    if (depth == UFO_BUFFER_DEPTH_8U) {
+        guint8 *src = (guint8 *) priv->host_array.data;
+
+        for (gint i = (n_pixels - 1); i >= 0; i--)
+            dst[i] = ((gfloat) src[i]);
+    }
+    else if (depth == UFO_BUFFER_DEPTH_16U) {
+        guint16 *src = (guint16 *) priv->host_array.data;
+
+        for (gint i = (n_pixels - 1); i >= 0; i--)
+            dst[i] = ((gfloat) src[i]);
+    }
+}
+
+/**
+ * ufo_buffer_param_spec:
+ * @name: canonical name of the property specified
+ * @nick: nick name for the property specified
+ * @blurb: description of the property specified
+ * @default_value: default value for the property specified
+ * @flags: flags for the property specified
+ *
+ * Creates a new #UfoBufferParamSpec instance specifying a #UFO_TYPE_BUFFER
+ * property.
+ *
+ * Returns: (transfer none): a newly created parameter specification
+ *
+ * @see g_param_spec_internal() for details on property names.
+ */
+GParamSpec *
+ufo_buffer_param_spec(const gchar *name, const gchar *nick, const gchar *blurb, UfoBuffer *default_value, GParamFlags flags)
+{
+    UfoBufferParamSpec *bspec;
+
+    bspec = g_param_spec_internal(UFO_TYPE_PARAM_BUFFER,
+            name, nick, blurb, flags);
+
+    return G_PARAM_SPEC(bspec);
+}
+
+static void
+ufo_buffer_finalize (GObject *gobject)
+{
+    UfoBuffer *buffer = UFO_BUFFER (gobject);
+    UfoBufferPrivate *priv = UFO_BUFFER_GET_PRIVATE (buffer);
+
+    g_free (priv->host_array.data);
+    priv->host_array.data = NULL;
+
+    if (priv->device_array != NULL) {
+        UFO_RESOURCES_CHECK_CLERR (clReleaseMemObject (priv->device_array));
+        priv->device_array = NULL;
+    }
+
+    if (priv->timer != NULL) {
+        g_timer_destroy (priv->timer);
+        priv->timer = NULL;
+    }
+
+    G_OBJECT_CLASS(ufo_buffer_parent_class)->finalize(gobject);
+}
+
+static void
+ufo_buffer_class_init (UfoBufferClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    gobject_class->finalize = ufo_buffer_finalize;
+
+    g_type_class_add_private(klass, sizeof(UfoBufferPrivate));
+}
+
+static void
+ufo_buffer_init (UfoBuffer *buffer)
+{
+    UfoBufferPrivate *priv;
+    buffer->priv = priv = UFO_BUFFER_GET_PRIVATE(buffer);
+    priv->last_queue = NULL;
+    priv->device_array = NULL;
+    priv->host_array.data = NULL;
+    priv->host_array.num_dims = 0;
+    priv->timer = g_timer_new ();
+    g_timer_stop (priv->timer);
+}
+
+static void
+ufo_buffer_param_init (GParamSpec *pspec)
+{
+    UfoBufferParamSpec *bspec = UFO_BUFFER_PARAM_SPEC(pspec);
+
+    bspec->default_value = NULL;
+}
+
+static void
+ufo_buffer_param_set_default (GParamSpec *pspec, GValue *value)
+{
+    UfoBufferParamSpec *bspec = UFO_BUFFER_PARAM_SPEC(pspec);
+
+    bspec->default_value = NULL;
+    g_value_unset(value);
+}
+
+GType
+ufo_buffer_param_get_type()
+{
+    static GType type = 0;
+
+    if (type == 0) {
+        GParamSpecTypeInfo pspec_info = {
+            sizeof(UfoBufferParamSpec),     /* instance_size */
+            16,                             /* n_preallocs */
+            ufo_buffer_param_init,          /* instance_init */
+            0,                              /* value_type */
+            NULL,                           /* finalize */
+            ufo_buffer_param_set_default,   /* value_set_default */
+            NULL,                           /* value_validate */
+            NULL,                           /* values_cmp */
+        };
+        pspec_info.value_type = UFO_TYPE_BUFFER;
+        type = g_param_type_register_static(g_intern_static_string("UfoBufferParam"), &pspec_info);
+        g_assert(type == UFO_TYPE_PARAM_BUFFER);
+    }
+
+    return type;
+}
diff --git a/ufo/ufo-buffer.h b/ufo/ufo-buffer.h
new file mode 100644
index 0000000..de6c0f6
--- /dev/null
+++ b/ufo/ufo-buffer.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_BUFFER_H
+#define __UFO_BUFFER_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_BUFFER             (ufo_buffer_get_type())
+#define UFO_BUFFER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_BUFFER, UfoBuffer))
+#define UFO_IS_BUFFER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_BUFFER))
+#define UFO_BUFFER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_BUFFER, UfoBufferClass))
+#define UFO_IS_BUFFER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_BUFFER))
+#define UFO_BUFFER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_BUFFER, UfoBufferClass))
+
+#define UFO_TYPE_PARAM_BUFFER       (ufo_buffer_param_get_type())
+#define UFO_IS_PARAM_SPEC_BUFFER(pspec)  (G_TYPE_CHECK_INSTANCE_TYPE((pspec), UFO_TYPE_PARAM_BUFFER))
+#define UFO_BUFFER_PARAM_SPEC(pspec)     (G_TYPE_CHECK_INSTANCE_CAST((pspec), UFO_TYPE_PARAM_BUFFER, UfoBufferParamSpec))
+
+/**
+ * UfoMemLocation:
+ * @UFO_LOCATION_INVALID: Memory is neither valid on host nor on device.
+ * @UFO_LOCATION_HOST: Memory is valid on host memory.
+ * @UFO_LOCATION_DEVICE: Memory is valid on device memory.
+ *
+ * Memory locations of a #UfoBuffer.
+ */
+typedef enum {
+    UFO_LOCATION_INVALID,
+    UFO_LOCATION_HOST,
+    UFO_LOCATION_DEVICE
+} UfoMemLocation;
+
+typedef struct _UfoBuffer           UfoBuffer;
+typedef struct _UfoBufferClass      UfoBufferClass;
+typedef struct _UfoBufferPrivate    UfoBufferPrivate;
+typedef struct _UfoBufferParamSpec  UfoBufferParamSpec;
+typedef struct _UfoRequisition      UfoRequisition;
+
+/**
+ * UfoBuffer:
+ *
+ * Represents n-dimensional data. The contents of the #UfoBuffer structure are
+ * private and should only be accessed via the provided API.
+ */
+struct _UfoBuffer {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoBufferPrivate *priv;
+};
+
+/**
+ * UFO_BUFFER_MAX_NDIMS:
+ *
+ * Maximum number of allowed dimensions. This is a pre-processor macro instead
+ * of const variable because of <ulink
+ * url="http://c-faq.com/ansi/constasconst.html">C constraints</ulink>.
+ */
+#define UFO_BUFFER_MAX_NDIMS 8
+
+/**
+ * UfoBufferClass:
+ *
+ * #UfoBuffer class
+ */
+struct _UfoBufferClass {
+    /*< private >*/
+    GObjectClass parent_class;
+};
+
+/**
+ * UfoRequisition:
+ * @n_dims: Number of dimensions
+ * @dims: Size of dimension
+ *
+ * Used to specify buffer size requirements.
+ */
+struct _UfoRequisition {
+    guint n_dims;
+    gsize dims[UFO_BUFFER_MAX_NDIMS];
+};
+
+/**
+ * UfoBufferParamSpec:
+ *
+ * UfoBufferParamSpec class
+ */
+struct _UfoBufferParamSpec {
+    /*< private >*/
+    GParamSpec  parent_instance;
+
+    UfoBuffer   *default_value;
+};
+
+/**
+ * UfoBufferDepth:
+ * @UFO_BUFFER_DEPTH_8U: 8 bit unsigned
+ * @UFO_BUFFER_DEPTH_16U: 16 bit unsigned
+ *
+ * Source depth of data as used in ufo_buffer_convert().
+ */
+typedef enum {
+    UFO_BUFFER_DEPTH_8U,
+    UFO_BUFFER_DEPTH_16U
+} UfoBufferDepth;
+
+UfoBuffer*  ufo_buffer_new                  (UfoRequisition *requisition,
+                                             gpointer        context);
+void        ufo_buffer_resize               (UfoBuffer      *buffer,
+                                             UfoRequisition *requisition);
+gint        ufo_buffer_cmp_dimensions       (UfoBuffer      *buffer,
+                                             UfoRequisition *requisition);
+void        ufo_buffer_get_requisition      (UfoBuffer      *buffer,
+                                             UfoRequisition *requisition);
+gsize       ufo_buffer_get_size             (UfoBuffer      *buffer);
+void        ufo_buffer_copy                 (UfoBuffer      *src,
+                                             UfoBuffer      *dst);
+UfoBuffer  *ufo_buffer_dup                  (UfoBuffer      *buffer);
+gfloat*     ufo_buffer_get_host_array       (UfoBuffer      *buffer,
+                                             gpointer        cmd_queue);
+gpointer    ufo_buffer_get_device_array     (UfoBuffer      *buffer,
+                                             gpointer        cmd_queue);
+void        ufo_buffer_discard_location     (UfoBuffer      *buffer,
+                                             UfoMemLocation  location);
+void        ufo_buffer_convert              (UfoBuffer      *buffer,
+                                             UfoBufferDepth  depth);
+GType       ufo_buffer_get_type             (void);
+
+GParamSpec* ufo_buffer_param_spec           (const gchar*   name,
+                                             const gchar*   nick,
+                                             const gchar*   blurb,
+                                             UfoBuffer*     default_value,
+                                             GParamFlags    flags);
+GType       ufo_buffer_param_get_type       (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-config.c b/ufo/ufo-config.c
new file mode 100644
index 0000000..afebe19
--- /dev/null
+++ b/ufo/ufo-config.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <ufo/ufo-config.h>
+#include <ufo/ufo-profiler.h>
+#include <ufo/ufo-enums.h>
+
+/**
+ * SECTION:ufo-config
+ * @Short_description: Access run-time specific settings
+ * @Title: UfoConfig
+ *
+ * A #UfoConfig object is used to keep settings that affect the run-time
+ * rather than the parameters of the filter graph. Each object that implements
+ * the #UfoConfigurable interface can receive a #UfoConfig object and use
+ * the information stored in it.
+ */
+
+G_DEFINE_TYPE(UfoConfig, ufo_config, G_TYPE_OBJECT)
+
+#define UFO_CONFIG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_CONFIG, UfoConfigPrivate))
+
+static void add_path (const gchar *path, UfoConfigPrivate *priv);
+
+enum {
+    PROP_0,
+    PROP_PATHS,
+    PROP_PROFILE_LEVEL,
+    PROP_PROFILE_OUTPUT_PREFIX,
+    N_PROPERTIES
+};
+
+struct _UfoConfigPrivate {
+    GValueArray         *path_array;
+    UfoProfilerLevel     profile_level;
+    gchar               *profile_output_prefix;
+};
+
+static GParamSpec *config_properties[N_PROPERTIES] = { NULL, };
+
+/**
+ * ufo_config_new:
+ *
+ * Create a config object.
+ *
+ * Return value: A new config object.
+ */
+UfoConfig *
+ufo_config_new (void)
+{
+    return UFO_CONFIG (g_object_new (UFO_TYPE_CONFIG, NULL));
+}
+
+/**
+ * ufo_config_get_paths:
+ * @config: A #UfoConfig object
+ *
+ * Get an array of path strings.
+ *
+ * Returns: (transfer full) (element-type utf8): A list of strings containing
+ * file system paths. Use g_list_free() to free it.
+ */
+GList *
+ufo_config_get_paths (UfoConfig *config)
+{
+    GValueArray *path_array;
+    GList *paths;
+    guint n_paths;
+
+    g_return_val_if_fail (UFO_IS_CONFIG (config), NULL);
+
+    path_array = config->priv->path_array;
+    n_paths = path_array->n_values;
+    paths = NULL;
+
+    for (guint i = 0; i < n_paths; i++) {
+        paths = g_list_append (paths,
+                               g_strdup (g_value_get_string (g_value_array_get_nth (path_array, i))));
+    }
+
+    return paths;
+}
+
+/**
+ * ufo_config_add_paths:
+ * @config: A #UfoConfig object
+ * @paths: (element-type utf8): List of strings
+ *
+ * Add @paths to the list of search paths for plugins and OpenCL kernel files.
+ */
+void
+ufo_config_add_paths (UfoConfig *config,
+                      GList *paths)
+{
+    g_return_if_fail (UFO_IS_CONFIG (config));
+    g_list_foreach (paths, (GFunc) add_path, config->priv);
+}
+
+static void
+add_path (const gchar *path,
+          UfoConfigPrivate *priv)
+{
+    GValue path_value = {0};
+
+    g_value_init (&path_value, G_TYPE_STRING);
+    g_value_set_string (&path_value, path);
+    g_value_array_prepend (priv->path_array, &path_value);
+    g_value_unset (&path_value);
+}
+
+static void
+ufo_config_set_property (GObject      *object,
+                         guint         property_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+    UfoConfigPrivate *priv = UFO_CONFIG_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_PATHS:
+            {
+                GValueArray *array;
+
+                if (priv->path_array != NULL)
+                    g_value_array_free (priv->path_array);
+
+                array = g_value_get_boxed (value);
+
+                if (array != NULL)
+                    priv->path_array = g_value_array_copy (array);
+            }
+            break;
+
+        case PROP_PROFILE_LEVEL:
+            priv->profile_level = g_value_get_flags (value);
+            break;
+
+        case PROP_PROFILE_OUTPUT_PREFIX:
+            g_free (priv->profile_output_prefix);
+            priv->profile_output_prefix = g_strdup (g_value_get_string (value));
+            break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_config_get_property (GObject      *object,
+                         guint         property_id,
+                         GValue       *value,
+                         GParamSpec   *pspec)
+{
+    UfoConfigPrivate *priv = UFO_CONFIG_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_PATHS:
+            g_value_set_boxed (value, priv->path_array);
+            break;
+
+        case PROP_PROFILE_LEVEL:
+            g_value_set_flags (value, priv->profile_level);
+            break;
+
+        case PROP_PROFILE_OUTPUT_PREFIX:
+            g_value_set_string (value, priv->profile_output_prefix);
+            break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_config_dispose (GObject *object)
+{
+    G_OBJECT_CLASS (ufo_config_parent_class)->finalize (object);
+    g_debug ("UfoConfig: disposed");
+}
+
+static void
+ufo_config_finalize (GObject *object)
+{
+    UfoConfigPrivate *priv = UFO_CONFIG_GET_PRIVATE (object);
+
+    g_value_array_free (priv->path_array);
+
+    G_OBJECT_CLASS (ufo_config_parent_class)->finalize (object);
+    g_debug ("UfoConfig: finalized");
+}
+
+static void
+ufo_config_class_init (UfoConfigClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    gobject_class->set_property = ufo_config_set_property;
+    gobject_class->get_property = ufo_config_get_property;
+    gobject_class->dispose      = ufo_config_dispose;
+    gobject_class->finalize     = ufo_config_finalize;
+
+    /**
+     * UfoConfig:paths:
+     *
+     * An array of strings with paths pointing to possible filter and kernel
+     * file locations.
+     */
+    config_properties[PROP_PATHS] =
+        g_param_spec_value_array ("paths",
+                                  "Array with paths",
+                                  "Array with paths",
+                                  g_param_spec_string ("path",
+                                                       "A path",
+                                                       "A path pointing to a filter or kernel",
+                                                       ".",
+                                                       G_PARAM_READWRITE),
+                                  G_PARAM_READWRITE);
+
+    /**
+     * UfoConfig:profile-level:
+     *
+     * Controls the amount of profiling.
+     *
+     * See: #UfoProfilerLevel for different levels of profiling.
+     */
+    config_properties[PROP_PROFILE_LEVEL] =
+        g_param_spec_flags ("profile-level",
+                            "Profiling level",
+                            "Profiling level",
+                            UFO_TYPE_PROFILER_LEVEL,
+                            UFO_PROFILER_LEVEL_NONE,
+                            G_PARAM_READWRITE);
+
+    config_properties[PROP_PROFILE_OUTPUT_PREFIX] =
+        g_param_spec_string ("profile-output-prefix",
+                             "Filename prefix for profiling output",
+                             "Filename prefix for profiling output. If NULL, information is output to stdout.",
+                             NULL,
+                             G_PARAM_READWRITE);
+
+    g_object_class_install_property (gobject_class, PROP_PATHS, config_properties[PROP_PATHS]);
+    g_object_class_install_property (gobject_class, PROP_PROFILE_LEVEL, config_properties[PROP_PROFILE_LEVEL]);
+    g_object_class_install_property (gobject_class, PROP_PROFILE_OUTPUT_PREFIX, config_properties[PROP_PROFILE_OUTPUT_PREFIX]);
+
+    g_type_class_add_private(klass, sizeof (UfoConfigPrivate));
+}
+
+static void
+ufo_config_init (UfoConfig *config)
+{
+    UfoConfigPrivate *priv;
+
+    config->priv = priv = UFO_CONFIG_GET_PRIVATE (config);
+    priv->path_array = g_value_array_new (0);
+    priv->profile_level = UFO_PROFILER_LEVEL_NONE;
+    priv->profile_output_prefix = NULL;
+
+    add_path ("/usr/local/lib64/ufo", priv);
+    add_path ("/usr/local/lib/ufo", priv);
+    add_path ("/usr/lib64/ufo", priv);
+    add_path ("/usr/lib/ufo", priv);
+    add_path (UFO_PLUGIN_DIR, priv);
+}
diff --git a/ufo/ufo-config.h b/ufo/ufo-config.h
new file mode 100644
index 0000000..447350e
--- /dev/null
+++ b/ufo/ufo-config.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_CONFIG_H
+#define __UFO_CONFIG_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_CONFIG             (ufo_config_get_type())
+#define UFO_CONFIG(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_CONFIG, UfoConfig))
+#define UFO_IS_CONFIG(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_CONFIG))
+#define UFO_CONFIG_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_CONFIG, UfoConfigClass))
+#define UFO_IS_CONFIG_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_CONFIG))
+#define UFO_CONFIG_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_CONFIG, UfoConfigClass))
+
+typedef struct _UfoConfig           UfoConfig;
+typedef struct _UfoConfigClass      UfoConfigClass;
+typedef struct _UfoConfigPrivate    UfoConfigPrivate;
+
+/**
+ * UfoConfig:
+ *
+ * A #UfoConfig provides access to run-time specific settings.
+ */
+struct _UfoConfig {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoConfigPrivate *priv;
+};
+
+/**
+ * UfoConfigClass:
+ *
+ * #UfoConfig class
+ */
+struct _UfoConfigClass {
+    /*< private >*/
+    GObjectClass parent_class;
+};
+
+UfoConfig   * ufo_config_new        (void);
+void          ufo_config_add_paths  (UfoConfig  *config,
+                                     GList      *paths);
+GList       * ufo_config_get_paths  (UfoConfig  *config);
+GType         ufo_config_get_type   (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-configurable.c b/ufo/ufo-configurable.c
new file mode 100644
index 0000000..7e6a260
--- /dev/null
+++ b/ufo/ufo-configurable.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ufo/ufo-configurable.h>
+#include <ufo/ufo-config.h>
+
+/**
+ * SECTION:ufo-configurable
+ * @Short_description: An interface for configurable objects
+ * @Title: UfoConfigurable
+ *
+ * An object that implements the #UfoConfigurable interface provides the common
+ * #UfoConfigurable:config property.
+ */
+
+typedef UfoConfigurableIface UfoConfigurableInterface;
+G_DEFINE_INTERFACE (UfoConfigurable, ufo_configurable, G_TYPE_OBJECT)
+
+static void
+ufo_configurable_default_init (UfoConfigurableInterface *iface)
+{
+    GParamSpec *config_spec;
+
+    /**
+     * UfoConfigurable:configuration:
+     *
+     * The #UfoConfiguration object that can be passed to all objects that
+     * implement the #UfoConfigurable interface.
+     */
+    config_spec = g_param_spec_object ("config",
+                                       "A UfoConfig object",
+                                       "A UfoConfig object",
+                                       UFO_TYPE_CONFIG,
+                                       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+
+    g_object_interface_install_property (iface, config_spec);
+}
diff --git a/ufo/ufo-configurable.h b/ufo/ufo-configurable.h
new file mode 100644
index 0000000..99e6adc
--- /dev/null
+++ b/ufo/ufo-configurable.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_CONFIGURABLE_H
+#define __UFO_CONFIGURABLE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_CONFIGURABLE             (ufo_configurable_get_type())
+#define UFO_CONFIGURABLE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_CONFIGURABLE, UfoConfigurable))
+#define UFO_CONFIGURABLE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_CONFIGURABLE, UfoConfigurableIface))
+#define UFO_IS_CONFIGURABLE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_CONFIGURABLE))
+#define UFO_IS_CONFIGURABLE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_CONFIGURABLE))
+#define UFO_CONFIGURABLE_GET_IFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE((inst), UFO_TYPE_CONFIGURABLE, UfoConfigurableIface))
+
+typedef struct _UfoConfigurable         UfoConfigurable;
+typedef struct _UfoConfigurableIface    UfoConfigurableIface;
+
+struct _UfoConfigurableIface {
+    /*< private >*/
+    GTypeInterface parent_iface;
+};
+
+GType ufo_configurable_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-cpu-node.c b/ufo/ufo-cpu-node.c
new file mode 100644
index 0000000..424d32f
--- /dev/null
+++ b/ufo/ufo-cpu-node.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <ufo/ufo-cpu-node.h>
+
+G_DEFINE_TYPE (UfoCpuNode, ufo_cpu_node, UFO_TYPE_NODE)
+
+#define UFO_CPU_NODE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_CPU_NODE, UfoCpuNodePrivate))
+
+
+struct _UfoCpuNodePrivate {
+    cpu_set_t *mask;
+};
+
+UfoNode *
+ufo_cpu_node_new (gpointer mask)
+{
+    UfoCpuNode *node;
+
+    g_return_val_if_fail (mask != NULL, NULL);
+    node = UFO_CPU_NODE (g_object_new (UFO_TYPE_CPU_NODE, NULL));
+    node->priv->mask = g_memdup (mask, sizeof(cpu_set_t));
+    return UFO_NODE (node);
+}
+
+/**
+ * ufo_cpu_node_get_affinity:
+ * @node: A #UfoCpuNode
+ *
+ * Get affinity mask of @node.
+ *
+ * Returns: (transfer none): A pointer to the cpu_set_t mask associated with
+ * @node.
+ */
+gpointer
+ufo_cpu_node_get_affinity (UfoCpuNode *node)
+{
+    g_return_val_if_fail (UFO_IS_CPU_NODE (node), NULL);
+    return node->priv->mask;
+}
+
+static void
+ufo_cpu_node_finalize (GObject *object)
+{
+    UfoCpuNodePrivate *priv;
+
+    priv = UFO_CPU_NODE_GET_PRIVATE (object);
+
+    if (priv->mask) {
+        g_free (priv->mask);
+        priv->mask = NULL;
+    }
+
+    G_OBJECT_CLASS (ufo_cpu_node_parent_class)->finalize (object);
+}
+
+static UfoNode *
+ufo_cpu_node_copy_real (UfoNode *node,
+                        GError **error)
+{
+    return UFO_NODE (ufo_cpu_node_new (UFO_CPU_NODE (node)->priv->mask));
+}
+
+static gboolean
+ufo_cpu_node_equal_real (UfoNode *n1,
+                         UfoNode *n2)
+{
+    UfoCpuNodePrivate *priv1;
+    UfoCpuNodePrivate *priv2;
+    const gsize MAX_CPUS = MIN (16, CPU_SETSIZE);
+
+    g_return_val_if_fail (UFO_IS_CPU_NODE (n1) && UFO_IS_CPU_NODE (n2), FALSE);
+    priv1 = UFO_CPU_NODE_GET_PRIVATE (n1);
+    priv2 = UFO_CPU_NODE_GET_PRIVATE (n2);
+
+    for (gsize i = 0; i < MAX_CPUS; i++) {
+        if (CPU_ISSET (i, priv1->mask) != CPU_ISSET (i, priv2->mask))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+ufo_cpu_node_class_init (UfoCpuNodeClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    UfoNodeClass *node_class = UFO_NODE_CLASS (klass);
+
+    object_class->finalize = ufo_cpu_node_finalize;
+    node_class->copy = ufo_cpu_node_copy_real;
+    node_class->equal = ufo_cpu_node_equal_real;
+
+    g_type_class_add_private(klass, sizeof(UfoCpuNodePrivate));
+}
+
+static void
+ufo_cpu_node_init (UfoCpuNode *self)
+{
+    UfoCpuNodePrivate *priv;
+    self->priv = priv = UFO_CPU_NODE_GET_PRIVATE (self);
+    priv->mask = NULL;
+}
diff --git a/ufo/ufo-cpu-node.h b/ufo/ufo-cpu-node.h
new file mode 100644
index 0000000..6ac4f7a
--- /dev/null
+++ b/ufo/ufo-cpu-node.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_CPU_NODE_H
+#define __UFO_CPU_NODE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-node.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_CPU_NODE             (ufo_cpu_node_get_type())
+#define UFO_CPU_NODE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_CPU_NODE, UfoCpuNode))
+#define UFO_IS_CPU_NODE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_CPU_NODE))
+#define UFO_CPU_NODE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_CPU_NODE, UfoCpuNodeClass))
+#define UFO_IS_CPU_NODE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_CPU_NODE))
+#define UFO_CPU_NODE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_CPU_NODE, UfoCpuNodeClass))
+
+typedef struct _UfoCpuNode           UfoCpuNode;
+typedef struct _UfoCpuNodeClass      UfoCpuNodeClass;
+typedef struct _UfoCpuNodePrivate    UfoCpuNodePrivate;
+
+/**
+ * UfoCpuNode:
+ *
+ * Main object for organizing filters. The contents of the #UfoCpuNode structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoCpuNode {
+    /*< private >*/
+    UfoNode parent_instance;
+
+    UfoCpuNodePrivate *priv;
+};
+
+/**
+ * UfoCpuNodeClass:
+ *
+ * #UfoCpuNode class
+ */
+struct _UfoCpuNodeClass {
+    /*< private >*/
+    UfoNodeClass parent_class;
+};
+
+UfoNode     *ufo_cpu_node_new           (gpointer mask);
+gpointer     ufo_cpu_node_get_affinity  (UfoCpuNode *node);
+GType        ufo_cpu_node_get_type      (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-cpu-task-iface.c b/ufo/ufo-cpu-task-iface.c
new file mode 100644
index 0000000..fe85176
--- /dev/null
+++ b/ufo/ufo-cpu-task-iface.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ufo/ufo-cpu-task-iface.h>
+
+typedef UfoCpuTaskIface UfoCpuTaskInterface;
+
+G_DEFINE_INTERFACE (UfoCpuTask, ufo_cpu_task, UFO_TYPE_TASK)
+
+
+gboolean
+ufo_cpu_task_process (UfoCpuTask *task,
+                      UfoBuffer **inputs,
+                      UfoBuffer *output,
+                      UfoRequisition *requisition)
+{
+    return UFO_CPU_TASK_GET_IFACE (task)->process (task, inputs, output, requisition);
+}
+
+void
+ufo_cpu_task_reduce (UfoCpuTask *task,
+                     UfoBuffer *output,
+                     UfoRequisition *requisition)
+{
+    UFO_CPU_TASK_GET_IFACE (task)->reduce (task, output, requisition);
+}
+
+gboolean
+ufo_cpu_task_generate (UfoCpuTask *task,
+                       UfoBuffer *output,
+                       UfoRequisition *requisition)
+{
+    return UFO_CPU_TASK_GET_IFACE (task)->generate (task, output, requisition);
+}
+
+static gboolean
+ufo_cpu_task_process_real (UfoCpuTask *task,
+                           UfoBuffer **inputs,
+                           UfoBuffer *output,
+                           UfoRequisition *requisition)
+{
+    g_warning ("`process' of UfoCpuTaskInterface not implemented");
+    return FALSE;
+}
+
+static void
+ufo_cpu_task_reduce_real (UfoCpuTask *task,
+                          UfoBuffer *output,
+                          UfoRequisition *requisition)
+{
+    g_warning ("`reduce' of UfoCpuTaskInterface not implemented");
+}
+
+static gboolean
+ufo_cpu_task_generate_real (UfoCpuTask *task,
+                            UfoBuffer *output,
+                            UfoRequisition *requisition)
+{
+    g_warning ("`generate' of UfoCpuTaskInterface not implemented");
+    return FALSE;
+}
+
+static void
+ufo_cpu_task_default_init (UfoCpuTaskInterface *iface)
+{
+    iface->process = ufo_cpu_task_process_real;
+    iface->reduce = ufo_cpu_task_reduce_real;
+    iface->generate = ufo_cpu_task_generate_real;
+}
diff --git a/ufo/ufo-cpu-task-iface.h b/ufo/ufo-cpu-task-iface.h
new file mode 100644
index 0000000..5cc2e64
--- /dev/null
+++ b/ufo/ufo-cpu-task-iface.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UFO_CPU_TASK_IFACE_H
+#define UFO_CPU_TASK_IFACE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-task-iface.h>
+#include <ufo/ufo-buffer.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_CPU_TASK             (ufo_cpu_task_get_type())
+#define UFO_CPU_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_CPU_TASK, UfoCpuTask))
+#define UFO_CPU_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_CPU_TASK, UfoCpuTaskIface))
+#define UFO_IS_CPU_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_CPU_TASK))
+#define UFO_IS_CPU_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_CPU_TASK))
+#define UFO_CPU_TASK_GET_IFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE((inst), UFO_TYPE_CPU_TASK, UfoCpuTaskIface))
+
+typedef struct _UfoCpuTask         UfoCpuTask;
+typedef struct _UfoCpuTaskIface    UfoCpuTaskIface;
+
+struct _UfoCpuTaskIface {
+    /*< private >*/
+    UfoTaskIface parent_iface;
+
+    gboolean (*process) (UfoCpuTask *task,
+                         UfoBuffer **inputs,
+                         UfoBuffer *output,
+                         UfoRequisition *requisition);
+    void     (*reduce)  (UfoCpuTask *task,
+                         UfoBuffer *output,
+                         UfoRequisition *requisition);
+    gboolean (*generate)(UfoCpuTask *task,
+                         UfoBuffer *output,
+                         UfoRequisition *requisition);
+};
+
+gboolean    ufo_cpu_task_process (UfoCpuTask     *task,
+                                  UfoBuffer     **inputs,
+                                  UfoBuffer      *output,
+                                  UfoRequisition *requisition);
+void        ufo_cpu_task_reduce  (UfoCpuTask     *task,
+                                  UfoBuffer      *output,
+                                  UfoRequisition *requisition);
+gboolean    ufo_cpu_task_generate(UfoCpuTask     *task,
+                                  UfoBuffer      *output,
+                                  UfoRequisition *requisition);
+
+GType ufo_cpu_task_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-dummy-task.c b/ufo/ufo-dummy-task.c
new file mode 100644
index 0000000..3fe3d4f
--- /dev/null
+++ b/ufo/ufo-dummy-task.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gmodule.h>
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#include <ufo/ufo-dummy-task.h>
+
+static void ufo_task_interface_init (UfoTaskIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (UfoDummyTask, ufo_dummy_task, UFO_TYPE_TASK_NODE,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_TASK,
+                                                ufo_task_interface_init))
+
+#define UFO_DUMMY_TASK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_DUMMY_TASK, UfoDummyTaskPrivate))
+
+enum {
+    PROP_0,
+    N_PROPERTIES
+};
+
+UfoNode *
+ufo_dummy_task_new (void)
+{
+    return UFO_NODE (g_object_new (UFO_TYPE_DUMMY_TASK, NULL));
+}
+
+static void
+ufo_dummy_task_setup (UfoTask *task,
+                      UfoResources *resources,
+                      GError **error)
+{
+}
+
+static void
+ufo_dummy_task_get_requisition (UfoTask *task,
+                                UfoBuffer **inputs,
+                                UfoRequisition *requisition)
+{
+}
+
+static void
+ufo_dummy_task_get_structure (UfoTask *task,
+                              guint *n_inputs,
+                              UfoInputParam **in_params,
+                              UfoTaskMode *mode)
+{
+}
+
+
+static void
+ufo_task_interface_init (UfoTaskIface *iface)
+{
+    iface->setup = ufo_dummy_task_setup;
+    iface->get_structure = ufo_dummy_task_get_structure;
+    iface->get_requisition = ufo_dummy_task_get_requisition;
+}
+
+static void
+ufo_dummy_task_class_init (UfoDummyTaskClass *klass)
+{
+}
+
+static void
+ufo_dummy_task_init (UfoDummyTask *task)
+{
+    ufo_task_node_set_plugin_name (UFO_TASK_NODE (task), "[dummy]");
+}
diff --git a/ufo/ufo-dummy-task.h b/ufo/ufo-dummy-task.h
new file mode 100644
index 0000000..d89e595
--- /dev/null
+++ b/ufo/ufo-dummy-task.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_DUMMY_TASK_H
+#define __UFO_DUMMY_TASK_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-task-node.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_DUMMY_TASK             (ufo_dummy_task_get_type())
+#define UFO_DUMMY_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_DUMMY_TASK, UfoDummyTask))
+#define UFO_IS_DUMMY_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_DUMMY_TASK))
+#define UFO_DUMMY_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_DUMMY_TASK, UfoDummyTaskClass))
+#define UFO_IS_DUMMY_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_DUMMY_TASK))
+#define UFO_DUMMY_TASK_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_DUMMY_TASK, UfoDummyTaskClass))
+
+typedef struct _UfoDummyTask           UfoDummyTask;
+typedef struct _UfoDummyTaskClass      UfoDummyTaskClass;
+typedef struct _UfoDummyTaskPrivate    UfoDummyTaskPrivate;
+
+/**
+ * UfoDummyTask:
+ *
+ * Main object for organizing filters. The contents of the #UfoDummyTask structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoDummyTask {
+    /*< private >*/
+    UfoTaskNode parent_instance;
+
+    UfoDummyTaskPrivate *priv;
+};
+
+/**
+ * UfoDummyTaskClass:
+ *
+ * #UfoDummyTask class
+ */
+struct _UfoDummyTaskClass {
+    /*< private >*/
+    UfoTaskNodeClass parent_class;
+};
+
+UfoNode   * ufo_dummy_task_new      (void);
+GType       ufo_dummy_task_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-enums.c.template b/ufo/ufo-enums.c.template
new file mode 100644
index 0000000..dbed629
--- /dev/null
+++ b/ufo/ufo-enums.c.template
@@ -0,0 +1,44 @@
+/*** BEGIN file-header ***/
+#include <config.h>
+
+#include "ufo-enums.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+#include "@filename@"
+/*** END file-production ***/
+
+
+/*** BEGIN value-header ***/
+GType
+ at enum_name@_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile)) {
+    static const G at Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+      { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+      { 0, NULL, NULL }
+    };
+    GType g_define_type_id =
+       g_ at type@_register_static (g_intern_static_string ("@EnumName@"), values);
+
+    g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+  }
+
+  return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+
+/*** END file-tail ***/
diff --git a/ufo/ufo-enums.h.template b/ufo/ufo-enums.h.template
new file mode 100644
index 0000000..6484bc7
--- /dev/null
+++ b/ufo/ufo-enums.h.template
@@ -0,0 +1,25 @@
+/*** BEGIN file-header ***/
+
+#ifndef UFO_ENUMS_H
+#define UFO_ENUMS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name at _get_type (void) G_GNUC_CONST;
+#define UFO_TYPE_ at ENUMSHORT@ (@enum_name at _get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* !UFO_ENUMS_H */
+/*** END file-tail ***/
diff --git a/ufo/ufo-gpu-node.c b/ufo/ufo-gpu-node.c
new file mode 100644
index 0000000..92af7e2
--- /dev/null
+++ b/ufo/ufo-gpu-node.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <CL/cl.h>
+#include <ufo/ufo-gpu-node.h>
+
+G_DEFINE_TYPE (UfoGpuNode, ufo_gpu_node, UFO_TYPE_NODE)
+
+#define UFO_GPU_NODE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_GPU_NODE, UfoGpuNodePrivate))
+
+
+struct _UfoGpuNodePrivate {
+    gpointer cmd_queue;
+};
+
+UfoNode *
+ufo_gpu_node_new (gpointer cmd_queue)
+{
+    UfoGpuNode *node;
+
+    g_return_val_if_fail (cmd_queue != NULL, NULL);
+    node = UFO_GPU_NODE (g_object_new (UFO_TYPE_GPU_NODE, NULL));
+    node->priv->cmd_queue = cmd_queue;
+    clRetainCommandQueue (cmd_queue);
+
+    return UFO_NODE (node);
+}
+
+/**
+ * ufo_gpu_node_get_cmd_queue:
+ * @node: A #UfoGpuNode
+ *
+ * Get command queue associated with @node.
+ *
+ * Returns: (transfer none): A cl_command_queue object for @node.
+ */
+gpointer
+ufo_gpu_node_get_cmd_queue (UfoGpuNode *node)
+{
+    g_return_val_if_fail (UFO_IS_GPU_NODE (node), NULL);
+    return node->priv->cmd_queue;
+}
+
+static UfoNode *
+ufo_gpu_node_copy_real (UfoNode *node,
+                        GError **error)
+{
+    return UFO_NODE (ufo_gpu_node_new (UFO_GPU_NODE (node)->priv->cmd_queue));
+}
+
+static gboolean
+ufo_gpu_node_equal_real (UfoNode *n1,
+                         UfoNode *n2)
+{
+    g_return_val_if_fail (UFO_IS_GPU_NODE (n1) && UFO_IS_GPU_NODE (n2), FALSE);
+    return UFO_GPU_NODE (n1)->priv->cmd_queue == UFO_GPU_NODE (n2)->priv->cmd_queue;
+}
+
+static void
+ufo_gpu_node_dispose (GObject *object)
+{
+    UfoGpuNodePrivate *priv;
+
+    priv = UFO_GPU_NODE_GET_PRIVATE (object);
+
+    if (priv->cmd_queue != NULL) {
+        g_debug ("Release cmd_queue=%p", priv->cmd_queue);
+        clReleaseCommandQueue (priv->cmd_queue);
+        priv->cmd_queue = NULL;
+    }
+
+    G_OBJECT_CLASS (ufo_gpu_node_parent_class)->dispose (object);
+}
+
+static void
+ufo_gpu_node_class_init (UfoGpuNodeClass *klass)
+{
+    GObjectClass *oclass = G_OBJECT_CLASS (klass);
+    UfoNodeClass *node_class = UFO_NODE_CLASS (klass);
+
+    oclass->dispose = ufo_gpu_node_dispose;
+    node_class->copy = ufo_gpu_node_copy_real;
+    node_class->equal = ufo_gpu_node_equal_real;
+
+    g_type_class_add_private (klass, sizeof (UfoGpuNodePrivate));
+}
+
+static void
+ufo_gpu_node_init (UfoGpuNode *self)
+{
+    UfoGpuNodePrivate *priv;
+    self->priv = priv = UFO_GPU_NODE_GET_PRIVATE (self);
+    priv->cmd_queue = NULL;
+}
diff --git a/ufo/ufo-gpu-node.h b/ufo/ufo-gpu-node.h
new file mode 100644
index 0000000..9c121a0
--- /dev/null
+++ b/ufo/ufo-gpu-node.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_GPU_NODE_H
+#define __UFO_GPU_NODE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-node.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_GPU_NODE             (ufo_gpu_node_get_type())
+#define UFO_GPU_NODE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_GPU_NODE, UfoGpuNode))
+#define UFO_IS_GPU_NODE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_GPU_NODE))
+#define UFO_GPU_NODE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_GPU_NODE, UfoGpuNodeClass))
+#define UFO_IS_GPU_NODE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_GPU_NODE))
+#define UFO_GPU_NODE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_GPU_NODE, UfoGpuNodeClass))
+
+typedef struct _UfoGpuNode           UfoGpuNode;
+typedef struct _UfoGpuNodeClass      UfoGpuNodeClass;
+typedef struct _UfoGpuNodePrivate    UfoGpuNodePrivate;
+
+/**
+ * UfoGpuNode:
+ *
+ * Main object for organizing filters. The contents of the #UfoGpuNode structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoGpuNode {
+    /*< private >*/
+    UfoNode parent_instance;
+
+    UfoGpuNodePrivate *priv;
+};
+
+/**
+ * UfoGpuNodeClass:
+ *
+ * #UfoGpuNode class
+ */
+struct _UfoGpuNodeClass {
+    /*< private >*/
+    UfoNodeClass parent_class;
+};
+
+UfoNode  *ufo_gpu_node_new              (gpointer    cmd_queue);
+gpointer  ufo_gpu_node_get_cmd_queue    (UfoGpuNode *node);
+GType     ufo_gpu_node_get_type         (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-gpu-task-iface.c b/ufo/ufo-gpu-task-iface.c
new file mode 100644
index 0000000..bc33ff4
--- /dev/null
+++ b/ufo/ufo-gpu-task-iface.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ufo/ufo-gpu-task-iface.h>
+
+typedef UfoGpuTaskIface UfoGpuTaskInterface;
+
+G_DEFINE_INTERFACE (UfoGpuTask, ufo_gpu_task, UFO_TYPE_TASK)
+
+gboolean
+ufo_gpu_task_process (UfoGpuTask *task,
+                      UfoBuffer **inputs,
+                      UfoBuffer *output,
+                      UfoRequisition *requisition,
+                      UfoGpuNode *node)
+{
+    return UFO_GPU_TASK_GET_IFACE (task)->process (task, inputs, output, requisition, node);
+}
+
+void
+ufo_gpu_task_reduce (UfoGpuTask *task,
+                     UfoBuffer *output,
+                     UfoRequisition *requisition,
+                     UfoGpuNode *node)
+{
+    UFO_GPU_TASK_GET_IFACE (task)->reduce (task, output, requisition, node);
+}
+
+gboolean
+ufo_gpu_task_generate (UfoGpuTask *task,
+                       UfoBuffer *output,
+                       UfoRequisition *requisition,
+                       UfoGpuNode *node)
+{
+    return UFO_GPU_TASK_GET_IFACE (task)->generate (task, output, requisition, node);
+}
+
+static gboolean
+ufo_gpu_task_process_real (UfoGpuTask *task,
+                           UfoBuffer **inputs,
+                           UfoBuffer *output,
+                           UfoRequisition *requisition,
+                           UfoGpuNode *node)
+{
+    g_warning ("`process' of UfoGpuTaskInterface not implemented");
+    return FALSE;
+}
+
+static void
+ufo_gpu_task_reduce_real (UfoGpuTask *task,
+                          UfoBuffer *output,
+                          UfoRequisition *requisition,
+                          UfoGpuNode *node)
+{
+    g_warning ("`reduce' of UfoGpuTaskInterface not implemented");
+}
+
+static gboolean
+ufo_gpu_task_generate_real (UfoGpuTask *task,
+                            UfoBuffer *output,
+                            UfoRequisition *requisition,
+                            UfoGpuNode *node)
+{
+    g_warning ("`generate' of UfoGpuTaskInterface not implemented");
+    return FALSE;
+}
+
+static void
+ufo_gpu_task_default_init (UfoGpuTaskInterface *iface)
+{
+    iface->process = ufo_gpu_task_process_real;
+    iface->reduce = ufo_gpu_task_reduce_real;
+    iface->generate = ufo_gpu_task_generate_real;
+}
diff --git a/ufo/ufo-gpu-task-iface.h b/ufo/ufo-gpu-task-iface.h
new file mode 100644
index 0000000..1a78f74
--- /dev/null
+++ b/ufo/ufo-gpu-task-iface.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UFO_GPU_TASK_IFACE_H
+#define UFO_GPU_TASK_IFACE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-gpu-node.h>
+#include <ufo/ufo-task-iface.h>
+#include <ufo/ufo-buffer.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_GPU_TASK             (ufo_gpu_task_get_type())
+#define UFO_GPU_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_GPU_TASK, UfoGpuTask))
+#define UFO_GPU_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_GPU_TASK, UfoGpuTaskIface))
+#define UFO_IS_GPU_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_GPU_TASK))
+#define UFO_IS_GPU_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_GPU_TASK))
+#define UFO_GPU_TASK_GET_IFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE((inst), UFO_TYPE_GPU_TASK, UfoGpuTaskIface))
+
+typedef struct _UfoGpuTask         UfoGpuTask;
+typedef struct _UfoGpuTaskIface    UfoGpuTaskIface;
+
+struct _UfoGpuTaskIface {
+    /*< private >*/
+    UfoTaskIface parent_iface;
+
+    gboolean (*process) (UfoGpuTask     *task,
+                         UfoBuffer     **inputs,
+                         UfoBuffer      *output,
+                         UfoRequisition *requisition,
+                         UfoGpuNode     *node);
+    void     (*reduce)  (UfoGpuTask     *task,
+                         UfoBuffer      *output,
+                         UfoRequisition *requisition,
+                         UfoGpuNode     *node);
+    gboolean (*generate)(UfoGpuTask     *task,
+                         UfoBuffer      *output,
+                         UfoRequisition *requisition,
+                         UfoGpuNode     *node);
+};
+
+gboolean ufo_gpu_task_process (UfoGpuTask       *task,
+                               UfoBuffer       **inputs,
+                               UfoBuffer        *output,
+                               UfoRequisition   *requisition,
+                               UfoGpuNode       *node);
+void     ufo_gpu_task_reduce  (UfoGpuTask       *task,
+                               UfoBuffer        *output,
+                               UfoRequisition   *requisition,
+                               UfoGpuNode       *node);
+gboolean ufo_gpu_task_generate(UfoGpuTask       *task,
+                               UfoBuffer        *output,
+                               UfoRequisition   *requisition,
+                               UfoGpuNode       *node);
+
+GType ufo_gpu_task_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-graph.c b/ufo/ufo-graph.c
new file mode 100644
index 0000000..57c2e5b
--- /dev/null
+++ b/ufo/ufo-graph.c
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <ufo/ufo-node.h>
+#include <ufo/ufo-graph.h>
+
+/**
+ * SECTION:ufo-graph
+ * @Short_description: Generic graph structure
+ * @Title: UfoGraph
+ */
+
+G_DEFINE_TYPE (UfoGraph, ufo_graph, G_TYPE_OBJECT)
+
+#define UFO_GRAPH_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_GRAPH, UfoGraphPrivate))
+
+struct _UfoGraphPrivate {
+    GList *node_types;
+    GList *nodes;
+    GList *edges;
+};
+
+enum {
+    PROP_0,
+    N_PROPERTIES
+};
+
+static gint cmp_edge (gconstpointer a, gconstpointer b);
+static gint cmp_edge_source (gconstpointer a, gconstpointer b);
+static gint cmp_edge_target (gconstpointer a, gconstpointer b);
+static UfoEdge *find_edge (GList *edges, UfoNode *source, UfoNode *target);
+static GList *g_list_find_all_data (GList *list, gconstpointer data, GCompareFunc func);
+
+/**
+ * ufo_graph_new:
+ *
+ * Create a new #UfoGraph object.
+ *
+ * Returns: (transfer full): A #UfoGraph.
+ */
+UfoGraph *
+ufo_graph_new (void)
+{
+    return UFO_GRAPH (g_object_new (UFO_TYPE_GRAPH, NULL));
+}
+
+/**
+ * ufo_graph_register_node_type:
+ * @graph: A #UfoGraph
+ * @type: A #GType
+ *
+ * Registers @type to be a valid node type of this graph. If a type has not be
+ * an added to @graph, any attempt to add such a node will fail.
+ */
+void
+ufo_graph_register_node_type (UfoGraph *graph,
+                              GType type)
+{
+    UfoGraphPrivate *priv;
+
+    g_return_if_fail (UFO_IS_GRAPH (graph));
+
+    /* Make sure type is at least a node type */
+    g_return_if_fail (g_type_is_a (type, UFO_TYPE_NODE));
+
+    priv = graph->priv;
+    priv->node_types = g_list_append (priv->node_types, GINT_TO_POINTER (type));
+}
+
+/**
+ * ufo_graph_get_registered_node_types:
+ * @graph: A #UfoGraph
+ *
+ * Get all types of nodes that can be added to @graph.
+ *
+ * Returns: (element-type GType) (transfer container): A list of #GType
+ * identifiers that can be added to @graph.
+ */
+GList *
+ufo_graph_get_registered_node_types (UfoGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    return g_list_copy (graph->priv->node_types);
+}
+
+/**
+ * ufo_graph_is_connected:
+ * @graph: A #UfoGraph
+ * @from: A source node
+ * @to: A target node
+ *
+ * Check whether @from is connected to @to.
+ *
+ * Returns: %TRUE if @from is connected to @to, otherwise %FALSE.
+ */
+gboolean
+ufo_graph_is_connected (UfoGraph *graph,
+                        UfoNode *from,
+                        UfoNode *to)
+{
+    UfoGraphPrivate *priv;
+    UfoEdge *edge;
+
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), FALSE);
+    priv = graph->priv;
+    edge = find_edge (priv->edges, from, to);
+    return edge != NULL;
+}
+
+static gboolean
+is_valid_node_type (UfoGraphPrivate *priv,
+                    UfoNode *node)
+{
+    for (GList *it = g_list_first (priv->node_types); it != NULL; it = g_list_next (it)) {
+        GType type = (GType) GPOINTER_TO_INT (it->data);
+
+        if (G_TYPE_CHECK_INSTANCE_TYPE (node, type))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void
+add_node_if_not_found (UfoGraphPrivate *priv,
+                       UfoNode *node)
+{
+    if (!g_list_find (priv->nodes, node)) {
+        priv->nodes = g_list_append (priv->nodes, node);
+        g_object_ref (node);
+    }
+}
+
+/**
+ * ufo_graph_connect_nodes:
+ * @graph: A #UfoGraph
+ * @source: A source node
+ * @target: A target node
+ * @label: An arbitrary label
+ *
+ * Connect @source with @target in @graph and annotate the edge with
+ * @label.
+ */
+void
+ufo_graph_connect_nodes (UfoGraph *graph,
+                         UfoNode *source,
+                         UfoNode *target,
+                         gpointer label)
+{
+    UfoGraphPrivate *priv;
+    UfoEdge *edge;
+
+    g_return_if_fail (UFO_IS_GRAPH (graph));
+    priv = graph->priv;
+
+    g_return_if_fail (is_valid_node_type (priv, source) && is_valid_node_type (priv, target));
+
+    edge = g_new0 (UfoEdge, 1);
+    edge->source = source;
+    edge->target = target;
+    edge->label = label;
+
+    priv->edges = g_list_append (priv->edges, edge);
+
+    add_node_if_not_found (priv, source);
+    add_node_if_not_found (priv, target);
+}
+
+/**
+ * ufo_graph_get_num_nodes:
+ * @graph: A #UfoGraph
+ *
+ * Get number of nodes in @graph. The number is always divisible by two, because
+ * nodes are only part of a graph if member of an edge.
+ * Returns: Number of nodes.
+ */
+guint
+ufo_graph_get_num_nodes (UfoGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), 0);
+    return g_list_length (graph->priv->nodes);
+}
+
+/**
+ * ufo_graph_get_num_edges:
+ * @graph: A #UfoGraph
+ *
+ * Get number of edges present in @graph.
+ *
+ * Returns: Number of edges.
+ */
+guint
+ufo_graph_get_num_edges (UfoGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), 0);
+    return g_list_length (graph->priv->edges);
+}
+
+/**
+ * ufo_graph_get_edges:
+ * @graph: A #UfoGraph
+ *
+ * Get all edges contained in @graph.
+ *
+ * Returns: (element-type UfoEdge): a list of #UfoEdge elements or %NULL on
+ * error. Release the list with g_list_free().
+ */
+GList *
+ufo_graph_get_edges (UfoGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    return g_list_copy (graph->priv->edges);
+}
+
+/**
+ * ufo_graph_get_nodes:
+ * @graph: A #UfoGraph
+ *
+ * Returns: (element-type UfoNode) (transfer container): A list of all nodes
+ * added to @graph.
+ */
+GList *
+ufo_graph_get_nodes (UfoGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    return g_list_copy (graph->priv->nodes);
+}
+
+/**
+ * ufo_graph_get_nodes_filtered:
+ * @graph: A #UfoGraph
+ * @func: (scope call): Predicate function to filter out nodes
+ * @user_data: Data to be passed to @func on invocation
+ *
+ * Get nodes filtered by the predicate @func.
+ *
+ * Returns: (element-type UfoNode) (transfer container): A list of all nodes
+ * that are marked as true by the predicate function @func.
+ */
+GList *
+ufo_graph_get_nodes_filtered (UfoGraph *graph,
+                              UfoFilterPredicate func,
+                              gpointer user_data)
+{
+    UfoGraphPrivate *priv;
+    GList *result = NULL;
+
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    priv = graph->priv;
+
+    for (GList *it = g_list_first (priv->nodes); it != NULL; it = g_list_next (it)) {
+        UfoNode *node = UFO_NODE (it->data);
+
+        if (func (node, user_data))
+            result = g_list_append (result, node);
+    }
+
+    return result;
+}
+
+/**
+ * ufo_graph_remove_edge:
+ * @graph: A #UfoGraph
+ * @source: A source node
+ * @target: A target node
+ *
+ * Remove edge between @source and @target.
+ */
+void
+ufo_graph_remove_edge (UfoGraph *graph,
+                       UfoNode *source,
+                       UfoNode *target)
+{
+    UfoGraphPrivate *priv;
+    UfoEdge *edge;
+
+    g_return_if_fail (UFO_IS_GRAPH (graph));
+    priv = graph->priv;
+    edge = find_edge (priv->edges, source, target);
+
+    if (edge != NULL) {
+        priv->nodes = g_list_remove (priv->nodes, source);
+        g_object_unref (source);
+
+        priv->nodes = g_list_remove (priv->nodes, target);
+        g_object_unref (target);
+
+        priv->edges = g_list_remove (priv->edges, edge);
+    }
+}
+
+/**
+ * ufo_graph_get_edge_label:
+ * @graph: A #UfoGraph
+ * @source: Source node
+ * @target: Target node
+ *
+ * Retrieve edge label between @source and @target.
+ *
+ * Returns: (transfer none): Edge label pointer.
+ */
+gpointer
+ufo_graph_get_edge_label (UfoGraph *graph,
+                          UfoNode *source,
+                          UfoNode *target)
+{
+    UfoGraphPrivate *priv;
+    UfoEdge *edge;
+
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    priv = graph->priv;
+    edge = find_edge (priv->edges, source, target);
+
+    if (edge != NULL)
+        return edge->label;
+
+    g_warning ("target not found");
+    return NULL;
+}
+
+static gboolean
+has_no_predecessor (UfoNode *node,
+                    UfoGraph *graph)
+{
+    for (GList *it = g_list_first (graph->priv->nodes); it != NULL; it = g_list_next (it)) {
+        UfoNode *source = (UfoNode *) it->data;
+
+        if (ufo_graph_is_connected (graph, source, node))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ * ufo_graph_get_roots:
+ * @graph: A #UfoGraph
+ *
+ * Get all roots of @graph.
+ *
+ * Returns: (element-type UfoNode) (transfer container): A list of all nodes
+ * that do not have a predessor node.
+ */
+GList *
+ufo_graph_get_roots (UfoGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    return ufo_graph_get_nodes_filtered (graph, (UfoFilterPredicate ) has_no_predecessor, graph);
+}
+
+static gboolean
+has_no_successor (UfoNode *node,
+                  UfoGraph *graph)
+{
+    for (GList *it = g_list_first (graph->priv->nodes); it != NULL; it = g_list_next (it)) {
+        UfoNode *target = (UfoNode *) it->data;
+
+        if (ufo_graph_is_connected (graph, node, target))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ * ufo_graph_get_leaves:
+ * @graph: A #UfoGraph
+ *
+ * Get all leaves of @graph.
+ *
+ * Returns: (element-type UfoNode) (transfer container): A list of all nodes
+ * that do not have a predessor node.
+ */
+GList *
+ufo_graph_get_leaves (UfoGraph *graph)
+{
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    return ufo_graph_get_nodes_filtered (graph, (UfoFilterPredicate) has_no_successor, graph);
+}
+
+/**
+ * ufo_graph_get_predecessors:
+ * @graph: A #UfoGraph
+ * @node: A #UfoNode whose predecessors are returned.
+ *
+ * Get the all nodes connected to @node.
+ *
+ * Returns: (element-type UfoNode) (transfer container): A list with preceeding
+ * nodes of @node. Free the list with g_list_free() but not its elements.
+ */
+GList *
+ufo_graph_get_predecessors (UfoGraph *graph,
+                            UfoNode *node)
+{
+    UfoGraphPrivate *priv;
+    UfoEdge match;
+    GList *edges;
+    GList *result;
+
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    priv = graph->priv;
+
+    match.target = node;
+    edges = g_list_find_all_data (priv->edges, &match, cmp_edge_target);
+    result = NULL;
+
+    for (GList *it = g_list_first (edges); it != NULL; it = g_list_next (it)) {
+        UfoEdge *edge = (UfoEdge *) it->data;
+        result = g_list_prepend (result, edge->source);
+    }
+
+    g_list_free (edges);
+    return result;
+}
+
+/**
+ * ufo_graph_get_successors:
+ * @graph: A #UfoGraph
+ * @node: A #UfoNode whose successors are returned.
+ *
+ * Get the successors of @node.
+ *
+ * Returns: (element-type UfoNode) (transfer container): A list with succeeding
+ * nodes of @node. Free the list with g_list_free() but not its elements.
+ */
+GList *
+ufo_graph_get_successors (UfoGraph *graph,
+                          UfoNode *node)
+{
+    UfoGraphPrivate *priv;
+    UfoEdge match;
+    GList *edges;
+    GList *result;
+
+    g_return_val_if_fail (UFO_IS_GRAPH (graph), NULL);
+    priv = graph->priv;
+
+    match.source = node;
+    edges = g_list_find_all_data (priv->edges, &match, cmp_edge_source);
+    result = NULL;
+
+    for (GList *it = g_list_first (edges); it != NULL; it = g_list_next (it)) {
+        UfoEdge *edge = (UfoEdge *) it->data;
+        result = g_list_append (result, edge->target);
+    }
+
+    g_list_free (edges);
+    return result;
+}
+
+/**
+ * ufo_graph_expand:
+ * @graph: A #UfoGraph
+ * @path: (element-type UfoNode): A path of nodes, preferably created with
+ * ufo_graph_get_paths().
+ *
+ * Duplicate nodes between head and tail of path and insert at the exact the
+ * position of where path started and ended.
+ */
+void
+ufo_graph_expand (UfoGraph *graph,
+                  GList *path)
+{
+    GList *head;
+    GList *tail;
+    UfoNode *orig;
+    UfoNode *current;
+    GError *error = NULL;
+
+    g_return_if_fail (UFO_IS_GRAPH (graph));
+
+    head = g_list_first (path);
+    tail = g_list_last (path);
+    g_assert (head != tail);
+
+    orig = UFO_NODE (head->data);
+
+    /* The first link goes from the original head */
+    current = orig;
+
+    for (GList *it = g_list_next (head); it != tail; it = g_list_next (it)) {
+        UfoNode *next;
+        UfoNode *copy;
+        gpointer label;
+
+        next = UFO_NODE (it->data);
+        copy = ufo_node_copy (next, &error);
+        label = ufo_graph_get_edge_label (graph, orig, next);
+        ufo_graph_connect_nodes (graph, current, copy, label);
+        current = copy;
+        orig = next;
+    }
+
+    if (tail->data != NULL) {
+        ufo_graph_connect_nodes (graph, current, UFO_NODE (tail->data),
+                                 ufo_graph_get_edge_label (graph, orig, UFO_NODE (tail->data)));
+    }
+}
+
+static void
+pickup_paths (UfoGraph *graph,
+              UfoFilterPredicate pred,
+              UfoNode *current,
+              UfoNode *last,
+              GList  *current_path,
+              GList **paths)
+{
+    GList *successors;
+
+    if (pred (current, NULL)) {
+        if (!pred (last, NULL))
+            current_path = g_list_append (current_path, last);
+
+        current_path = g_list_append (current_path, current);
+    }
+    else {
+        if (current_path != NULL) {
+            current_path = g_list_append (current_path, current);
+            *paths = g_list_append (*paths, current_path);
+        }
+
+        current_path = NULL;
+    }
+
+    successors = ufo_graph_get_successors (graph, current);
+
+    for (GList *it = g_list_first (successors); it != NULL; it = g_list_next (it))
+        pickup_paths (graph, pred, it->data, current, g_list_copy (current_path), paths);
+
+    g_list_free (successors);
+}
+
+/**
+ * ufo_graph_get_paths:
+ * @graph: A #UfoGraph
+ * @pred: (scope call): A predicate function
+ *
+ * Compute a list of lists that contain complete paths with nodes that match a
+ * predicate function.
+ *
+ * Returns: (element-type GLib.GList) (transfer full): A list of lists with paths
+ * that match @pred.
+ */
+GList *
+ufo_graph_get_paths (UfoGraph *graph,
+                     UfoFilterPredicate pred)
+{
+    GList *roots;
+    GList *paths = NULL;
+
+    roots = ufo_graph_get_roots (graph);
+
+    for (GList *it = g_list_first (roots); it != NULL; it = g_list_next (it)) {
+        UfoNode *node = UFO_NODE (it->data);
+        pickup_paths (graph, pred, node, node, NULL, &paths);
+    }
+
+    g_list_free (roots);
+    return paths;
+}
+
+/**
+ * ufo_graph_dump_dot:
+ * @graph: A #UfoGraph
+ * @filename: A string containing a filename
+ *
+ * Stores a GraphViz dot representation of @graph in @filename.
+ */
+void
+ufo_graph_dump_dot (UfoGraph *graph,
+                    const gchar *filename)
+{
+    FILE *fp;
+    GList *nodes;
+
+    fp = fopen (filename, "w");
+    fprintf (fp, "digraph foo {\n");
+
+    nodes = ufo_graph_get_nodes (graph);
+
+    for (GList *it = g_list_first (nodes); it != NULL; it = g_list_next (it)) {
+        UfoNode *source;
+        GList *successors;
+
+        source = UFO_NODE (it->data);
+        successors = ufo_graph_get_successors (graph, source);
+
+        for (GList *jt = g_list_first (successors); jt != NULL; jt = g_list_next (jt)) {
+            UfoNode *target;
+
+            target = UFO_NODE (jt->data);
+
+            fprintf (fp, "  %s_%p -> %s_%p;\n",
+                     g_type_name (G_TYPE_FROM_INSTANCE (source)), (gpointer) source,
+                     g_type_name (G_TYPE_FROM_INSTANCE (target)), (gpointer) target);
+        }
+
+        g_list_free (successors);
+    }
+
+    g_list_free (nodes);
+    fprintf (fp, "}\n");
+    fclose (fp);
+}
+
+static gint
+cmp_edge (gconstpointer a, gconstpointer b)
+{
+    const UfoEdge *edge_a = a;
+    const UfoEdge *edge_b = b;
+
+    if ((edge_a->source == edge_b->source) &&
+        (edge_a->target == edge_b->target)) {
+        return 0;
+    }
+
+    return -1;
+}
+
+static gint
+cmp_edge_source (gconstpointer a, gconstpointer b)
+{
+    const UfoEdge *edge_a = a;
+    const UfoEdge *edge_b = b;
+
+    if (edge_a->source == edge_b->source)
+        return 0;
+
+    return -1;
+}
+
+static gint
+cmp_edge_target (gconstpointer a, gconstpointer b)
+{
+    const UfoEdge *edge_a = a;
+    const UfoEdge *edge_b = b;
+
+    if (edge_a->target == edge_b->target)
+        return 0;
+
+    return -1;
+}
+
+static GList *
+g_list_find_all_data (GList *list,
+                      gconstpointer data,
+                      GCompareFunc func)
+{
+    GList *result = NULL;
+
+    for (GList *it = g_list_first (list); it != NULL; it = g_list_next (it)) {
+        if (func (data, it->data) == 0)
+            result = g_list_prepend (result, it->data);
+    }
+
+    return result;
+}
+
+static UfoEdge *
+find_edge (GList *edges,
+           UfoNode *source,
+           UfoNode *target)
+{
+    UfoEdge search_edge;
+    UfoEdge *edge;
+    GList *result;
+
+    search_edge.source = source;
+    search_edge.target = target;
+    result = g_list_find_custom (edges, &search_edge, cmp_edge);
+    edge = result != NULL ? g_list_nth_data (result, 0) : NULL;
+    return edge;
+}
+
+static void
+ufo_graph_dispose (GObject *object)
+{
+    UfoGraphPrivate *priv;
+
+    priv = UFO_GRAPH_GET_PRIVATE (object);
+
+    if (priv->edges != NULL) {
+        g_list_foreach (priv->edges, (GFunc) g_free, NULL);
+        g_list_free (priv->edges);
+        priv->edges = NULL;
+    }
+
+    if (priv->nodes != NULL) {
+        g_list_foreach (priv->nodes, (GFunc) g_object_unref, NULL);
+        g_list_free (priv->nodes);
+        priv->nodes = NULL;
+    }
+
+    G_OBJECT_CLASS (ufo_graph_parent_class)->dispose (object);
+}
+
+static void
+ufo_graph_finalize (GObject *object)
+{
+    UfoGraphPrivate *priv;
+
+    priv = UFO_GRAPH_GET_PRIVATE (object);
+    g_list_free (priv->node_types);
+    priv->node_types = NULL;
+
+    G_OBJECT_CLASS (ufo_graph_parent_class)->finalize (object);
+}
+
+static void
+ufo_graph_class_init (UfoGraphClass *klass)
+{
+    GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+    oclass->dispose  = ufo_graph_dispose;
+    oclass->finalize = ufo_graph_finalize;
+
+    g_type_class_add_private(klass, sizeof(UfoGraphPrivate));
+}
+
+static void
+ufo_graph_init (UfoGraph *self)
+{
+    UfoGraphPrivate *priv;
+    self->priv = priv = UFO_GRAPH_GET_PRIVATE (self);
+    priv->node_types = NULL;
+    priv->nodes = NULL;
+}
diff --git a/ufo/ufo-graph.h b/ufo/ufo-graph.h
new file mode 100644
index 0000000..325a6f2
--- /dev/null
+++ b/ufo/ufo-graph.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_GRAPH_H
+#define __UFO_GRAPH_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-node.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_GRAPH             (ufo_graph_get_type())
+#define UFO_GRAPH(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_GRAPH, UfoGraph))
+#define UFO_IS_GRAPH(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_GRAPH))
+#define UFO_GRAPH_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_GRAPH, UfoGraphClass))
+#define UFO_IS_GRAPH_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_GRAPH))
+#define UFO_GRAPH_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_GRAPH, UfoGraphClass))
+
+typedef struct _UfoGraph           UfoGraph;
+typedef struct _UfoGraphClass      UfoGraphClass;
+typedef struct _UfoGraphPrivate    UfoGraphPrivate;
+typedef struct _UfoEdge            UfoEdge;
+
+typedef gboolean (*UfoFilterPredicate) (UfoNode *node, gpointer user_data);
+
+/**
+ * UfoEdge:
+ * @source: source node
+ * @target: target node
+ * @label: label
+ *
+ * An edge in a #UfoGraph.
+ */
+struct _UfoEdge {
+    UfoNode     *source;
+    UfoNode     *target;
+    gpointer     label;
+};
+
+/**
+ * UfoGraph:
+ *
+ * Main object for organizing filters. The contents of the #UfoGraph structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoGraph {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoGraphPrivate *priv;
+};
+
+/**
+ * UfoGraphClass:
+ *
+ * #UfoGraph class
+ */
+struct _UfoGraphClass {
+    /*< private >*/
+    GObjectClass parent_class;
+};
+
+UfoGraph   *ufo_graph_new                   (void);
+void        ufo_graph_register_node_type    (UfoGraph       *graph,
+                                             GType           type);
+GList      *ufo_graph_get_registered_node_types
+                                            (UfoGraph       *graph);
+void        ufo_graph_connect_nodes         (UfoGraph       *graph,
+                                             UfoNode        *source,
+                                             UfoNode        *target,
+                                             gpointer        label);
+gboolean    ufo_graph_is_connected          (UfoGraph       *graph,
+                                             UfoNode        *from,
+                                             UfoNode        *to);
+void        ufo_graph_remove_edge           (UfoGraph       *graph,
+                                             UfoNode        *source,
+                                             UfoNode        *target);
+gpointer    ufo_graph_get_edge_label        (UfoGraph       *graph,
+                                             UfoNode        *source,
+                                             UfoNode        *target);
+guint       ufo_graph_get_num_nodes         (UfoGraph       *graph);
+GList      *ufo_graph_get_nodes             (UfoGraph       *graph);
+GList      *ufo_graph_get_nodes_filtered    (UfoGraph       *graph,
+                                             UfoFilterPredicate func,
+                                             gpointer        user_data);
+guint       ufo_graph_get_num_edges         (UfoGraph       *graph);
+GList      *ufo_graph_get_edges             (UfoGraph       *graph);
+GList      *ufo_graph_get_roots             (UfoGraph       *graph);
+GList      *ufo_graph_get_leaves            (UfoGraph       *graph);
+GList      *ufo_graph_get_predecessors      (UfoGraph       *graph,
+                                             UfoNode        *node);
+GList      *ufo_graph_get_successors        (UfoGraph       *graph,
+                                             UfoNode        *node);
+GList      *ufo_graph_get_paths             (UfoGraph       *graph,
+                                             UfoFilterPredicate pred);
+void        ufo_graph_expand                (UfoGraph       *graph,
+                                             GList          *path);
+void        ufo_graph_dump_dot              (UfoGraph       *graph,
+                                             const gchar    *filename);
+GType       ufo_graph_get_type              (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-group.c b/ufo/ufo-group.c
new file mode 100644
index 0000000..0e5a694
--- /dev/null
+++ b/ufo/ufo-group.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <CL/cl.h>
+#include <ufo/ufo-group.h>
+#include <ufo/ufo-task-node.h>
+
+G_DEFINE_TYPE (UfoGroup, ufo_group, G_TYPE_OBJECT)
+
+#define UFO_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_GROUP, UfoGroupPrivate))
+
+typedef struct {
+    GAsyncQueue *queues[2];
+    guint        capacity;
+} UfoQueue;
+
+struct _UfoGroupPrivate {
+    GList           *targets;
+    guint            n_targets;
+    UfoQueue       **queues;
+    gint            *n_expected;
+    gint             n_received;
+    gboolean        *ready;
+    UfoSendPattern   pattern;
+    guint            current;
+    cl_context       context;
+    GList           *buffers;
+};
+
+enum {
+    PROP_0,
+    N_PROPERTIES
+};
+
+typedef enum {
+    UFO_QUEUE_PRODUCER = 0,
+    UFO_QUEUE_CONSUMER = 1
+} UfoQueueAccess;
+
+static UfoQueue     *ufo_queue_new          (void);
+static gpointer      ufo_queue_pop          (UfoQueue *queue, UfoQueueAccess access);
+static void          ufo_queue_push         (UfoQueue *queue, UfoQueueAccess access, gpointer data);
+static void          ufo_queue_insert       (UfoQueue *queue, UfoQueueAccess access, gpointer data);
+static guint         ufo_queue_get_capacity (UfoQueue *queue);
+
+/**
+ * ufo_group_new:
+ * @targets: (element-type UfoNode): A list of #UfoNode targets
+ * @context: A cl_context on which the targets should operate on.
+ * @pattern: Pattern to distribute data among the @targets
+ *
+ * Create a new #UfoGroup.
+ *
+ * Returns: A new #UfoGroup.
+ */
+UfoGroup *
+ufo_group_new (GList *targets,
+               gpointer context,
+               UfoSendPattern pattern)
+{
+    UfoGroup *group;
+    UfoGroupPrivate *priv;
+
+    group = UFO_GROUP (g_object_new (UFO_TYPE_GROUP, NULL));
+    priv = group->priv;
+
+    priv->targets = g_list_copy (targets);
+    priv->n_targets = g_list_length (targets);
+    priv->queues = g_new0 (UfoQueue *, priv->n_targets);
+    priv->n_expected = g_new0 (gint, priv->n_targets);
+    priv->pattern = pattern;
+    priv->current = 0;
+    priv->context = context;
+    priv->n_received = 0;
+
+    for (guint i = 0; i < priv->n_targets; i++)
+        priv->queues[i] = ufo_queue_new ();
+
+    return group;
+}
+
+static UfoBuffer *
+pop_or_alloc_buffer (UfoGroupPrivate *priv,
+                     guint pos,
+                     UfoRequisition *requisition)
+{
+    UfoBuffer *buffer;
+
+    if (ufo_queue_get_capacity (priv->queues[pos]) < priv->n_targets) {
+        buffer = ufo_buffer_new (requisition, priv->context);
+        priv->buffers = g_list_append (priv->buffers, buffer);
+        ufo_queue_insert (priv->queues[pos], UFO_QUEUE_PRODUCER, buffer);
+    }
+
+    buffer = ufo_queue_pop (priv->queues[pos], UFO_QUEUE_PRODUCER);
+
+    if (ufo_buffer_cmp_dimensions (buffer, requisition))
+        ufo_buffer_resize (buffer, requisition);
+
+    return buffer;
+}
+
+/**
+ * ufo_group_pop_output_buffer:
+ * @group: A #UfoGroup
+ * @requisition: Size of the buffer.
+ *
+ * Return value: (transfer full): A newly allocated buffer or a re-used buffer
+ * that must be released with ufo_group_push_output_buffer().
+ */
+UfoBuffer *
+ufo_group_pop_output_buffer (UfoGroup *group,
+                             UfoRequisition *requisition)
+{
+    UfoGroupPrivate *priv;
+    guint pos;
+
+    priv = group->priv;
+    pos = priv->pattern == UFO_SEND_SCATTER || UFO_SEND_SEQUENTIAL ? priv->current : 0;
+
+    return pop_or_alloc_buffer (priv, pos, requisition);
+}
+
+void
+ufo_group_push_output_buffer (UfoGroup *group,
+                              UfoBuffer *buffer)
+{
+    UfoGroupPrivate *priv;
+
+    priv = group->priv;
+    priv->n_received++;
+
+    /* Copy or not depending on the send pattern */
+    if (priv->pattern == UFO_SEND_SCATTER) {
+        ufo_queue_push (priv->queues[priv->current],
+                        UFO_QUEUE_PRODUCER,
+                        buffer);
+
+        priv->current = (priv->current + 1) % priv->n_targets;
+    }
+    else if (priv->pattern == UFO_SEND_BROADCAST) {
+        UfoRequisition requisition;
+
+        ufo_buffer_get_requisition (buffer, &requisition);
+
+        for (guint pos = 1; pos < priv->n_targets; pos++) {
+            UfoBuffer *copy;
+
+            copy = pop_or_alloc_buffer (priv, pos, &requisition);
+            ufo_buffer_copy (buffer, copy);
+            ufo_queue_push (priv->queues[pos], UFO_QUEUE_PRODUCER, copy);
+        }
+
+        ufo_queue_push (priv->queues[0], UFO_QUEUE_PRODUCER, buffer);
+    }
+    else if (priv->pattern == UFO_SEND_SEQUENTIAL) {
+        ufo_queue_push (priv->queues[priv->current],
+                        UFO_QUEUE_PRODUCER,
+                        buffer);
+
+        if (priv->n_expected[priv->current] == priv->n_received) {
+            ufo_queue_push (priv->queues[priv->current],
+                            UFO_QUEUE_PRODUCER,
+                            UFO_END_OF_STREAM);
+            /* FIXME: setting priv->current to 0 again wouldn't be right */
+            priv->current = (priv->current + 1) % priv->n_targets;
+            priv->n_received = 0;
+        }
+    }
+}
+
+void
+ufo_group_set_num_expected (UfoGroup *group,
+                            UfoTask *target,
+                            gint n_expected)
+{
+    UfoGroupPrivate *priv;
+    gint pos;
+
+    g_return_if_fail (UFO_IS_GROUP (group));
+    priv = group->priv;
+    pos = g_list_index (priv->targets, target);
+    priv->n_expected[pos] = n_expected;
+}
+
+/**
+ * ufo_group_pop_input_buffer:
+ * @group: A #UfoGroup
+ * @target: The #UfoTask that is a target in @group
+ *
+ * Return value: (transfer full): A buffer that must be released with
+ * ufo_group_push_input_buffer().
+ */
+UfoBuffer *
+ufo_group_pop_input_buffer (UfoGroup *group,
+                            UfoTask *target)
+{
+    UfoGroupPrivate *priv;
+    UfoBuffer *input;
+    gint pos;
+
+    priv = group->priv;
+    pos = g_list_index (priv->targets, target);
+    input = pos >= 0 ? ufo_queue_pop (priv->queues[pos], UFO_QUEUE_CONSUMER) : NULL;
+
+    return input;
+}
+
+void
+ufo_group_push_input_buffer (UfoGroup *group,
+                             UfoTask *target,
+                             UfoBuffer *input)
+{
+    UfoGroupPrivate *priv;
+    gint pos;
+
+    priv = group->priv;
+    pos = g_list_index (priv->targets, target);
+
+    if (pos >= 0)
+        ufo_queue_push (priv->queues[pos], UFO_QUEUE_CONSUMER, input);
+}
+
+void
+ufo_group_finish (UfoGroup *group)
+{
+    UfoGroupPrivate *priv;
+
+    priv = group->priv;
+
+    for (guint i = 0; i < priv->n_targets; i++) {
+        ufo_queue_push (priv->queues[i],
+                        UFO_QUEUE_PRODUCER,
+                        UFO_END_OF_STREAM);
+    }
+}
+
+static UfoQueue *
+ufo_queue_new (void)
+{
+    UfoQueue *queue = g_new0 (UfoQueue, 1);
+    queue->queues[0] = g_async_queue_new ();
+    queue->queues[1] = g_async_queue_new ();
+    queue->capacity  = 0;
+    return queue;
+}
+
+static void
+ufo_queue_free (UfoQueue *queue)
+{
+    g_async_queue_unref (queue->queues[0]);
+    g_async_queue_unref (queue->queues[1]);
+    g_free (queue);
+}
+
+static gpointer
+ufo_queue_pop (UfoQueue *queue, UfoQueueAccess access)
+{
+    return g_async_queue_pop (queue->queues[access]);
+}
+
+static void
+ufo_queue_push (UfoQueue *queue, UfoQueueAccess access, gpointer data)
+{
+    g_async_queue_push (queue->queues[1-access], data);
+}
+
+static void
+ufo_queue_insert (UfoQueue *queue, UfoQueueAccess access, gpointer data)
+{
+    g_async_queue_push (queue->queues[access], data);
+    queue->capacity++;
+}
+
+static guint
+ufo_queue_get_capacity (UfoQueue *queue)
+{
+    return queue->capacity;
+}
+
+static void
+ufo_group_dispose(GObject *object)
+{
+    UfoGroupPrivate *priv;
+
+    priv = UFO_GROUP_GET_PRIVATE (object);
+    g_list_foreach (priv->buffers, (GFunc) g_object_unref, NULL);
+    G_OBJECT_CLASS (ufo_group_parent_class)->dispose (object);
+}
+
+static void
+ufo_group_finalize (GObject *object)
+{
+    UfoGroupPrivate *priv;
+
+    priv = UFO_GROUP_GET_PRIVATE (object);
+
+    g_list_free (priv->targets);
+    priv->targets = NULL;
+
+    g_list_free (priv->buffers);
+    priv->buffers = NULL;
+
+    for (guint i = 0; i < priv->n_targets; i++)
+        ufo_queue_free (priv->queues[i]);
+
+    g_free (priv->queues);
+    priv->queues = NULL;
+
+    G_OBJECT_CLASS (ufo_group_parent_class)->finalize (object);
+}
+
+static void
+ufo_group_class_init (UfoGroupClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+    gobject_class->dispose  = ufo_group_dispose;
+    gobject_class->finalize = ufo_group_finalize;
+
+    g_type_class_add_private(klass, sizeof(UfoGroupPrivate));
+}
+
+static void
+ufo_group_init (UfoGroup *self)
+{
+    UfoGroupPrivate *priv;
+    self->priv = priv = UFO_GROUP_GET_PRIVATE (self);
+    priv->buffers = NULL;
+}
diff --git a/ufo/ufo-group.h b/ufo/ufo-group.h
new file mode 100644
index 0000000..e4e7e32
--- /dev/null
+++ b/ufo/ufo-group.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_GROUP_H
+#define __UFO_GROUP_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-task-iface.h>
+#include <ufo/ufo-buffer.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_GROUP             (ufo_group_get_type())
+#define UFO_GROUP(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_GROUP, UfoGroup))
+#define UFO_IS_GROUP(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_GROUP))
+#define UFO_GROUP_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_GROUP, UfoGroupClass))
+#define UFO_IS_GROUP_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_GROUP))
+#define UFO_GROUP_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_GROUP, UfoGroupClass))
+
+typedef struct _UfoGroup           UfoGroup;
+typedef struct _UfoGroupClass      UfoGroupClass;
+typedef struct _UfoGroupPrivate    UfoGroupPrivate;
+
+#define UFO_END_OF_STREAM (GINT_TO_POINTER(1))
+
+/**
+ * UfoSendPattern:
+ * @UFO_SEND_BROADCAST: Broadcast data to all connected nodes
+ * @UFO_SEND_SCATTER: Scatter data among connected nodes.
+ * @UFO_SEND_SEQUENTIAL: Break up a linear input stream and transfer sub streams
+ * one by one to connected nodes.
+ *
+ * The send pattern describes how results are passed to connected nodes.
+ */
+typedef enum {
+    UFO_SEND_BROADCAST,
+    UFO_SEND_SCATTER,
+    UFO_SEND_SEQUENTIAL
+} UfoSendPattern;
+
+/**
+ * UfoGroup:
+ *
+ * Main object for organizing filters. The contents of the #UfoGroup structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoGroup {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoGroupPrivate *priv;
+};
+
+/**
+ * UfoGroupClass:
+ *
+ * #UfoGroup class
+ */
+struct _UfoGroupClass {
+    /*< private >*/
+    GObjectClass parent_class;
+};
+
+UfoGroup  * ufo_group_new                   (GList          *targets,
+                                             gpointer        context,
+                                             UfoSendPattern  pattern);
+void        ufo_group_set_num_expected      (UfoGroup       *group,
+                                             UfoTask        *target,
+                                             gint            n_expected);
+UfoBuffer * ufo_group_pop_output_buffer     (UfoGroup       *group,
+                                             UfoRequisition *requisition);
+void        ufo_group_push_output_buffer    (UfoGroup       *group,
+                                             UfoBuffer      *buffer);
+UfoBuffer * ufo_group_pop_input_buffer      (UfoGroup       *group,
+                                             UfoTask        *target);
+void        ufo_group_push_input_buffer     (UfoGroup       *group,
+                                             UfoTask        *target,
+                                             UfoBuffer      *input);
+void        ufo_group_finish                (UfoGroup       *group);
+GType       ufo_group_get_type              (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-input-task.c b/ufo/ufo-input-task.c
new file mode 100644
index 0000000..68f0cad
--- /dev/null
+++ b/ufo/ufo-input-task.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+#include <gmodule.h>
+#include <ufo/ufo-input-task.h>
+#include <ufo/ufo-cpu-task-iface.h>
+#include <ufo/ufo-gpu-task-iface.h>
+#include <ufo/ufo-gpu-node.h>
+
+/**
+ * SECTION:ufo-input-task
+ * @Short_description: An input task
+ * @Title: UfoInputTask
+ */
+
+struct _UfoInputTaskPrivate {
+    GAsyncQueue *in_queue;
+    GAsyncQueue *out_queue;
+    UfoTaskMode mode;
+    gboolean active;
+    guint n_inputs;
+    UfoInputParam *in_params;
+    UfoBuffer *input;
+};
+
+static void ufo_task_interface_init (UfoTaskIface *iface);
+static void ufo_cpu_task_interface_init (UfoCpuTaskIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (UfoInputTask, ufo_input_task, UFO_TYPE_TASK_NODE,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_TASK,
+                                                ufo_task_interface_init)
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_CPU_TASK,
+                                                ufo_cpu_task_interface_init))
+
+#define UFO_INPUT_TASK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_INPUT_TASK, UfoInputTaskPrivate))
+
+enum {
+    PROP_0,
+    N_PROPERTIES
+};
+
+UfoNode *
+ufo_input_task_new (void)
+{
+    UfoInputTask *task;
+    UfoInputTaskPrivate *priv;
+
+    task = UFO_INPUT_TASK (g_object_new (UFO_TYPE_INPUT_TASK, NULL));
+    priv = task->priv;
+
+    /* TODO: free in_params and queues */
+    priv->in_queue = g_async_queue_new ();
+    priv->out_queue = g_async_queue_new ();
+    priv->active = TRUE;
+
+    return UFO_NODE (task);
+}
+
+void
+ufo_input_task_stop (UfoInputTask *task)
+{
+    g_return_if_fail (UFO_IS_INPUT_TASK (task));
+    task->priv->active = FALSE;
+}
+
+void
+ufo_input_task_release_input_buffer (UfoInputTask *task,
+                                     UfoBuffer *buffer)
+{
+    g_return_if_fail (UFO_IS_INPUT_TASK (task));
+    g_async_queue_push (task->priv->in_queue, buffer);
+}
+
+/**
+ * ufo_input_task_get_input_buffer:
+ * @task: A #UfoInputTask
+ *
+ * Get the input buffer to which we write the data received from the master
+ * remote node.
+ *
+ * Return value: (transfer full): A #UfoBuffer for writing input data.
+ */
+UfoBuffer *
+ufo_input_task_get_input_buffer (UfoInputTask *task)
+{
+    g_return_val_if_fail (UFO_IS_INPUT_TASK (task), NULL);
+    return g_async_queue_pop (task->priv->out_queue);
+}
+
+static void
+ufo_input_task_setup (UfoTask *task,
+                      UfoResources *resources,
+                      GError **error)
+{
+}
+
+static void
+ufo_input_task_get_structure (UfoTask *task,
+                              guint *n_inputs,
+                              UfoInputParam **in_params,
+                              UfoTaskMode *mode)
+{
+    *n_inputs = 0;
+    *mode = UFO_TASK_MODE_SINGLE;
+}
+
+static void
+ufo_input_task_get_requisition (UfoTask *task,
+                                UfoBuffer **none,
+                                UfoRequisition *requisition)
+{
+    UfoInputTaskPrivate *priv;
+
+    priv = UFO_INPUT_TASK_GET_PRIVATE (task);
+
+    /* Pop input here but release later in ufo_input_task_process */
+    priv->input = g_async_queue_pop (priv->in_queue);
+    ufo_buffer_get_requisition (priv->input, requisition);
+}
+
+static gboolean
+ufo_input_task_process (UfoCpuTask *task,
+                        UfoBuffer **none,
+                        UfoBuffer *output,
+                        UfoRequisition *requisition)
+{
+    UfoInputTaskPrivate *priv;
+
+    g_return_val_if_fail (UFO_IS_INPUT_TASK (task), FALSE);
+    priv = UFO_INPUT_TASK_GET_PRIVATE (task);
+
+    if (!priv->active)
+        return FALSE;
+
+    ufo_buffer_discard_location (output, UFO_LOCATION_DEVICE);
+    ufo_buffer_copy (priv->input, output);
+
+    /* input was popped in ufo_input_task_get_requisition */
+    g_async_queue_push (priv->out_queue, priv->input);
+
+    return priv->active;
+}
+
+static void
+ufo_input_task_finalize (GObject *object)
+{
+    UfoInputTaskPrivate *priv;
+
+    priv = UFO_INPUT_TASK_GET_PRIVATE (object);
+    g_async_queue_unref (priv->in_queue);
+    g_async_queue_unref (priv->out_queue);
+}
+
+static void
+ufo_task_interface_init (UfoTaskIface *iface)
+{
+    iface->setup = ufo_input_task_setup;
+    iface->get_structure = ufo_input_task_get_structure;
+    iface->get_requisition = ufo_input_task_get_requisition;
+}
+
+static void
+ufo_cpu_task_interface_init (UfoCpuTaskIface *iface)
+{
+    iface->process = ufo_input_task_process;
+}
+
+static void
+ufo_input_task_class_init (UfoInputTaskClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->finalize = ufo_input_task_finalize;
+
+    g_type_class_add_private (gobject_class, sizeof(UfoInputTaskPrivate));
+}
+
+static void
+ufo_input_task_init (UfoInputTask *task)
+{
+    task->priv = UFO_INPUT_TASK_GET_PRIVATE (task);
+    ufo_task_node_set_plugin_name (UFO_TASK_NODE (task), "input-task");
+}
diff --git a/ufo/ufo-input-task.h b/ufo/ufo-input-task.h
new file mode 100644
index 0000000..276d752
--- /dev/null
+++ b/ufo/ufo-input-task.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_INPUT_TASK_H
+#define __UFO_INPUT_TASK_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-task-node.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_INPUT_TASK             (ufo_input_task_get_type())
+#define UFO_INPUT_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_INPUT_TASK, UfoInputTask))
+#define UFO_IS_INPUT_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_INPUT_TASK))
+#define UFO_INPUT_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_INPUT_TASK, UfoInputTaskClass))
+#define UFO_IS_INPUT_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_INPUT_TASK))
+#define UFO_INPUT_TASK_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_INPUT_TASK, UfoInputTaskClass))
+
+typedef struct _UfoInputTask           UfoInputTask;
+typedef struct _UfoInputTaskClass      UfoInputTaskClass;
+typedef struct _UfoInputTaskPrivate    UfoInputTaskPrivate;
+
+/**
+ * UfoInputTask:
+ *
+ * Main object for organizing filters. The contents of the #UfoInputTask structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoInputTask {
+    /*< private >*/
+    UfoTaskNode parent_instance;
+
+    UfoInputTaskPrivate *priv;
+};
+
+/**
+ * UfoInputTaskClass:
+ *
+ * #UfoInputTask class
+ */
+struct _UfoInputTaskClass {
+    /*< private >*/
+    UfoTaskNodeClass parent_class;
+};
+
+UfoNode   * ufo_input_task_new                  (void);
+void        ufo_input_task_stop                 (UfoInputTask *task);
+void        ufo_input_task_release_input_buffer (UfoInputTask *task,
+                                                 UfoBuffer *buffer);
+UfoBuffer * ufo_input_task_get_input_buffer     (UfoInputTask *task);
+GType       ufo_input_task_get_type             (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-node.c b/ufo/ufo-node.c
new file mode 100644
index 0000000..a60e690
--- /dev/null
+++ b/ufo/ufo-node.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ufo/ufo-node.h>
+
+/**
+ * SECTION:ufo-node
+ * @Short_description: Generic node that can be connected in a #UfoGraph
+ * @Title: UfoNode
+ */
+
+G_DEFINE_TYPE (UfoNode, ufo_node, G_TYPE_OBJECT)
+
+#define UFO_NODE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_NODE, UfoNodePrivate))
+
+struct _UfoNodePrivate {
+    UfoNode  *copied_from;
+    gpointer  label;
+};
+
+enum {
+    PROP_0,
+    N_PROPERTIES
+};
+
+UfoNode *
+ufo_node_new (gpointer label)
+{
+    UfoNode *node;
+
+    node = UFO_NODE (g_object_new (UFO_TYPE_NODE, NULL));
+    node->priv->label = label;
+    return node;
+}
+
+/**
+ * ufo_node_get_label:
+ * @node: A #UfoNode
+ *
+ * Get arbitrary label data of @node.
+ *
+ * Returns: (transfer none): The label of @node.
+ */
+gpointer
+ufo_node_get_label (UfoNode *node)
+{
+    g_return_val_if_fail (UFO_IS_NODE (node), NULL);
+    return node->priv->label;
+}
+
+static UfoNode *
+ufo_node_copy_real (UfoNode *node,
+                    GError **error)
+{
+    GObject *copy;
+    GParamSpec **props;
+    guint n_props;
+
+    copy = g_object_new (G_OBJECT_TYPE (node), NULL);
+    props = g_object_class_list_properties (G_OBJECT_GET_CLASS (node), &n_props);
+
+    for (guint i = 0; i < n_props; i++) {
+        GValue value = {0};
+
+        g_value_init (&value, props[i]->value_type);
+        g_object_get_property (G_OBJECT (node), props[i]->name, &value);
+        g_object_set_property (G_OBJECT (copy), props[i]->name, &value);
+    }
+
+    g_free (props);
+    return UFO_NODE (copy);
+}
+
+static gboolean
+ufo_node_equal_real (UfoNode *n1,
+                     UfoNode *n2)
+{
+    g_return_val_if_fail (UFO_IS_NODE (n1) && UFO_IS_NODE (n2), FALSE);
+
+    /* FIXME: When done we should just check if the types match */
+    return n1 == n2 ||
+           n1->priv->copied_from == n2 ||
+           n2->priv->copied_from == n1;
+}
+
+/**
+ * ufo_node_copy:
+ * @node: A #UfoNode
+ * @error: Location for an error
+ *
+ * Get a copy of @node. How "deep" the copy is, depends on the inherited
+ * implementation of @node.
+ *
+ * Returns: (transfer full): Copy of @node.
+ */
+UfoNode *
+ufo_node_copy (UfoNode *node,
+               GError **error)
+{
+    UfoNode *offspring;
+
+    offspring = UFO_NODE_GET_CLASS (node)->copy (node, error);
+    offspring->priv->copied_from = node;
+    return offspring;
+}
+
+gboolean
+ufo_node_equal (UfoNode *n1,
+                UfoNode *n2)
+{
+    return UFO_NODE_GET_CLASS (n1)->equal (n1, n2);
+}
+
+static void
+ufo_node_class_init (UfoNodeClass *klass)
+{
+    klass->copy = ufo_node_copy_real;
+    klass->equal = ufo_node_equal_real;
+
+    g_type_class_add_private(klass, sizeof(UfoNodePrivate));
+}
+
+static void
+ufo_node_init (UfoNode *self)
+{
+    UfoNodePrivate *priv;
+    self->priv = priv = UFO_NODE_GET_PRIVATE (self);
+
+    priv->copied_from = NULL;
+    priv->label = NULL;
+}
diff --git a/ufo/ufo-node.h b/ufo/ufo-node.h
new file mode 100644
index 0000000..9d5eb18
--- /dev/null
+++ b/ufo/ufo-node.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_NODE_H
+#define __UFO_NODE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_NODE             (ufo_node_get_type())
+#define UFO_NODE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_NODE, UfoNode))
+#define UFO_IS_NODE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_NODE))
+#define UFO_NODE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_NODE, UfoNodeClass))
+#define UFO_IS_NODE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_NODE))
+#define UFO_NODE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_NODE, UfoNodeClass))
+
+typedef struct _UfoNode           UfoNode;
+typedef struct _UfoNodeClass      UfoNodeClass;
+typedef struct _UfoNodePrivate    UfoNodePrivate;
+
+/**
+ * UfoNode:
+ *
+ * Main object for organizing filters. The contents of the #UfoNode structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoNode {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoNodePrivate *priv;
+};
+
+/**
+ * UfoNodeClass:
+ *
+ * #UfoNode class
+ */
+struct _UfoNodeClass {
+    /*< private >*/
+    GObjectClass parent_class;
+
+    UfoNode * (*copy)   (UfoNode   *node,
+                         GError   **error);
+    gboolean  (*equal)  (UfoNode   *n1,
+                         UfoNode   *n2);
+};
+
+UfoNode     *ufo_node_new       (gpointer    label);
+gpointer     ufo_node_get_label (UfoNode    *node);
+UfoNode     *ufo_node_copy      (UfoNode    *node,
+                                 GError    **error);
+gboolean     ufo_node_equal     (UfoNode    *n1,
+                                 UfoNode    *n2);
+GType        ufo_node_get_type  (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-output-task.c b/ufo/ufo-output-task.c
new file mode 100644
index 0000000..a14f6c4
--- /dev/null
+++ b/ufo/ufo-output-task.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gmodule.h>
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#include <ufo/ufo-output-task.h>
+#include <ufo-cpu-task-iface.h>
+
+/**
+ * SECTION:ufo-output-task
+ * @Short_description: Output task
+ * @Title: UfoOutputTask
+ */
+
+struct _UfoOutputTaskPrivate {
+    GAsyncQueue *out_queue;
+    GAsyncQueue *in_queue;
+    guint n_dims;
+    guint n_copies;
+    GList *copies;
+};
+
+static void ufo_task_interface_init (UfoTaskIface *iface);
+static void ufo_cpu_task_interface_init (UfoCpuTaskIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (UfoOutputTask, ufo_output_task, UFO_TYPE_TASK_NODE,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_TASK,
+                                                ufo_task_interface_init)
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_CPU_TASK,
+                                                ufo_cpu_task_interface_init))
+
+#define UFO_OUTPUT_TASK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_OUTPUT_TASK, UfoOutputTaskPrivate))
+
+enum {
+    PROP_0,
+    N_PROPERTIES
+};
+
+UfoNode *
+ufo_output_task_new (guint n_dims)
+{
+    UfoOutputTask *task = UFO_OUTPUT_TASK (g_object_new (UFO_TYPE_OUTPUT_TASK, NULL));
+
+    task->priv->n_dims = n_dims;
+    return UFO_NODE (task);
+}
+
+void
+ufo_output_task_get_output_requisition (UfoOutputTask *task,
+                                        UfoRequisition *requisition)
+{
+    UfoBuffer *buffer;
+    UfoOutputTaskPrivate *priv;
+
+    g_return_if_fail (UFO_IS_OUTPUT_TASK (task));
+    priv = task->priv;
+    buffer = g_async_queue_pop (priv->out_queue);
+    ufo_buffer_get_requisition (buffer, requisition);
+    g_async_queue_push (priv->out_queue, buffer);
+}
+
+/**
+ * ufo_output_task_get_output_buffer:
+ * @task: A #UfoInputTask
+ *
+ * Get the output buffer from which we read the data to be sent to the master
+ * remote node.
+ *
+ * Return value: (transfer full): A #UfoBuffer for reading output data.
+ */
+UfoBuffer *
+ufo_output_task_get_output_buffer (UfoOutputTask *task)
+{
+    g_return_val_if_fail (UFO_IS_OUTPUT_TASK (task), NULL);
+    return g_async_queue_pop (task->priv->out_queue);
+}
+
+void
+ufo_output_task_release_output_buffer (UfoOutputTask *task,
+                                       UfoBuffer *buffer)
+{
+    g_return_if_fail (UFO_IS_OUTPUT_TASK (task));
+    g_async_queue_push (task->priv->in_queue, buffer);
+}
+
+static void
+ufo_output_task_setup (UfoTask *task,
+                       UfoResources *resources,
+                       GError **error)
+{
+}
+
+static void
+ufo_output_task_get_requisition (UfoTask *task,
+                                 UfoBuffer **inputs,
+                                 UfoRequisition *requisition)
+{
+    (*requisition).n_dims = 0;
+}
+
+static void
+ufo_output_task_get_structure (UfoTask *task,
+                               guint *n_inputs,
+                               UfoInputParam **in_params,
+                               UfoTaskMode *mode)
+{
+    UfoOutputTaskPrivate *priv = UFO_OUTPUT_TASK_GET_PRIVATE (task);
+
+    *mode = UFO_TASK_MODE_SINGLE;
+    *n_inputs = 1;
+    *in_params = g_new0 (UfoInputParam, 1);
+    (*in_params)[0].n_dims = priv->n_dims;
+}
+
+static gboolean
+ufo_output_task_process (UfoCpuTask *task,
+                         UfoBuffer **outputs,
+                         UfoBuffer *output,
+                         UfoRequisition *requisition)
+{
+    UfoOutputTaskPrivate *priv;
+    UfoRequisition req;
+    UfoBuffer *copy;
+
+    g_return_val_if_fail (UFO_IS_OUTPUT_TASK (task), FALSE);
+    ufo_buffer_get_requisition (outputs[0], &req);
+
+    priv = UFO_OUTPUT_TASK_GET_PRIVATE (task);
+
+    if (priv->n_copies == 0) {
+        copy = ufo_buffer_dup (outputs[0]);
+        g_async_queue_push (priv->in_queue, copy);
+        priv->copies = g_list_append (priv->copies, copy);
+        priv->n_copies++;
+    }
+
+    copy = g_async_queue_pop (priv->in_queue);
+    ufo_buffer_copy (outputs[0], copy);
+    g_async_queue_push (priv->out_queue, copy);
+    return TRUE;
+}
+
+static void
+ufo_output_task_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    switch (property_id) {
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_output_task_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    switch (property_id) {
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_output_task_dispose (GObject *object)
+{
+    UfoOutputTaskPrivate *priv;
+
+    priv = UFO_OUTPUT_TASK_GET_PRIVATE (object);
+    g_list_foreach (priv->copies, (GFunc) g_object_unref, NULL);
+
+    G_OBJECT_CLASS (ufo_output_task_parent_class)->dispose (object);
+}
+
+static void
+ufo_output_task_finalize (GObject *object)
+{
+    UfoOutputTaskPrivate *priv;
+
+    priv = UFO_OUTPUT_TASK_GET_PRIVATE (object);
+    g_list_free (priv->copies);
+    priv->copies = NULL;
+
+    G_OBJECT_CLASS (ufo_output_task_parent_class)->finalize (object);
+}
+
+static void
+ufo_task_interface_init (UfoTaskIface *iface)
+{
+    iface->setup = ufo_output_task_setup;
+    iface->get_structure = ufo_output_task_get_structure;
+    iface->get_requisition = ufo_output_task_get_requisition;
+}
+
+static void
+ufo_cpu_task_interface_init (UfoCpuTaskIface *iface)
+{
+    iface->process = ufo_output_task_process;
+}
+
+static void
+ufo_output_task_class_init (UfoOutputTaskClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->set_property = ufo_output_task_set_property;
+    gobject_class->get_property = ufo_output_task_get_property;
+    gobject_class->dispose = ufo_output_task_dispose;
+    gobject_class->finalize = ufo_output_task_finalize;
+
+    g_type_class_add_private (gobject_class, sizeof(UfoOutputTaskPrivate));
+}
+
+static void
+ufo_output_task_init (UfoOutputTask *task)
+{
+    task->priv = UFO_OUTPUT_TASK_GET_PRIVATE (task);
+    task->priv->out_queue = g_async_queue_new ();
+    task->priv->in_queue = g_async_queue_new ();
+    task->priv->n_copies = 0;
+    task->priv->copies = NULL;
+
+    ufo_task_node_set_plugin_name (UFO_TASK_NODE (task), "output-task");
+}
diff --git a/ufo/ufo-output-task.h b/ufo/ufo-output-task.h
new file mode 100644
index 0000000..e1d07ff
--- /dev/null
+++ b/ufo/ufo-output-task.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_OUTPUT_TASK_H
+#define __UFO_OUTPUT_TASK_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-task-node.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_OUTPUT_TASK             (ufo_output_task_get_type())
+#define UFO_OUTPUT_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_OUTPUT_TASK, UfoOutputTask))
+#define UFO_IS_OUTPUT_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_OUTPUT_TASK))
+#define UFO_OUTPUT_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_OUTPUT_TASK, UfoOutputTaskClass))
+#define UFO_IS_OUTPUT_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_OUTPUT_TASK))
+#define UFO_OUTPUT_TASK_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_OUTPUT_TASK, UfoOutputTaskClass))
+
+typedef struct _UfoOutputTask           UfoOutputTask;
+typedef struct _UfoOutputTaskClass      UfoOutputTaskClass;
+typedef struct _UfoOutputTaskPrivate    UfoOutputTaskPrivate;
+
+/**
+ * UfoOutputTask:
+ *
+ * Main object for organizing filters. The contents of the #UfoOutputTask structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoOutputTask {
+    /*< private >*/
+    UfoTaskNode parent_instance;
+
+    UfoOutputTaskPrivate *priv;
+};
+
+/**
+ * UfoOutputTaskClass:
+ *
+ * #UfoOutputTask class
+ */
+struct _UfoOutputTaskClass {
+    /*< private >*/
+    UfoTaskNodeClass parent_class;
+};
+
+UfoNode   * ufo_output_task_new                     (guint n_dims);
+void        ufo_output_task_get_output_requisition  (UfoOutputTask *task,
+                                                     UfoRequisition *requisition);
+UfoBuffer * ufo_output_task_get_output_buffer       (UfoOutputTask *task);
+void        ufo_output_task_release_output_buffer   (UfoOutputTask *task,
+                                                     UfoBuffer *buffer);
+GType       ufo_output_task_get_type                (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-plugin-manager.c b/ufo/ufo-plugin-manager.c
new file mode 100644
index 0000000..d45ea6c
--- /dev/null
+++ b/ufo/ufo-plugin-manager.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <gmodule.h>
+#include <glob.h>
+#include <ufo/ufo-plugin-manager.h>
+#include <ufo/ufo-configurable.h>
+#include <ufo/ufo-task-node.h>
+#include <ufo/ufo-dummy-task.h>
+
+/**
+ * SECTION:ufo-plugin-manager
+ * @Short_description: Load an #UfoFilter from a shared object
+ * @Title: UfoPluginManager
+ *
+ * The plugin manager opens shared object modules searched for in locations
+ * specified with ufo_plugin_manager_add_paths(). An #UfoFilter can be
+ * instantiated with ufo_plugin_manager_get_filter() with a one-to-one mapping
+ * between filter name xyz and module name libfilterxyz.so. Any errors are
+ * reported as one of #UfoPluginManagerError codes.
+ */
+
+G_DEFINE_TYPE_WITH_CODE (UfoPluginManager, ufo_plugin_manager, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_CONFIGURABLE, NULL))
+
+#define UFO_PLUGIN_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_PLUGIN_MANAGER, UfoPluginManagerPrivate))
+
+typedef UfoNode* (* NewFunc) (void);
+
+struct _UfoPluginManagerPrivate {
+    GList       *search_paths;
+    GSList      *modules;
+    GHashTable  *new_funcs;  /**< maps from gchar* to NewFunc* */
+};
+
+enum {
+    PROP_0,
+    PROP_CONFIG,
+    N_PROPERTIES
+};
+
+/**
+ * UfoPluginManagerError:
+ * @UFO_PLUGIN_MANAGER_ERROR_MODULE_NOT_FOUND: The module could not be found
+ * @UFO_PLUGIN_MANAGER_ERROR_MODULE_OPEN: Module could not be opened
+ * @UFO_PLUGIN_MANAGER_ERROR_SYMBOL_NOT_FOUND: Necessary entry symbol was not
+ *      found
+ *
+ * Possible errors that ufo_plugin_manager_get_filter() can return.
+ */
+GQuark
+ufo_plugin_manager_error_quark (void)
+{
+    return g_quark_from_static_string ("ufo-plugin-manager-error-quark");
+}
+
+static gchar *
+plugin_manager_get_path (UfoPluginManagerPrivate *priv, const gchar *name)
+{
+    /* Check first if filename is already a path */
+    if (g_path_is_absolute (name)) {
+        if (g_file_test (name, G_FILE_TEST_EXISTS))
+            return g_strdup (name);
+        else
+            return NULL;
+    }
+
+    /* If it is not a path, search in all known paths */
+    for (GList *p = g_list_first (priv->search_paths); p != NULL; p = g_list_next (p)) {
+        gchar *path = g_build_filename ((gchar *) p->data, name, NULL);
+
+        if (g_file_test (path, G_FILE_TEST_EXISTS))
+            return path;
+
+        g_free (path);
+    }
+
+    return NULL;
+}
+
+static void
+copy_config_paths (UfoPluginManagerPrivate *priv, UfoConfig *config)
+{
+    priv->search_paths = g_list_concat (ufo_config_get_paths (config), priv->search_paths);
+}
+
+/**
+ * ufo_plugin_manager_new:
+ * @config: (allow-none): A #UfoConfig object or %NULL.
+ *
+ * Create a plugin manager object to instantiate filter objects. When a config
+ * object is passed to the constructor, its search-path property is added to the
+ * internal search paths.
+ *
+ * Return value: A new plugin manager object.
+ */
+UfoPluginManager *
+ufo_plugin_manager_new (UfoConfig *config)
+{
+    UfoPluginManager *manager;
+
+    manager = UFO_PLUGIN_MANAGER (g_object_new (UFO_TYPE_PLUGIN_MANAGER,
+                                                "config", config,
+                                                NULL));
+
+    return manager;
+}
+
+static gchar *
+transform_string (const gchar *pattern,
+                  const gchar *s,
+                  const gchar *separator)
+{
+    gchar **sv;
+    gchar *transformed;
+    gchar *result;
+
+    sv = g_strsplit_set (s, "-_ ", -1);
+    transformed = g_strjoinv (separator, sv);
+    result = g_strdup_printf (pattern, transformed);
+
+    g_strfreev (sv);
+    g_free (transformed);
+    return result;
+}
+
+/**
+ * ufo_plugin_manager_get_task:
+ * @manager: A #UfoPluginManager
+ * @name: Name of the plugin.
+ * @error: return location for a GError or %NULL
+ *
+ * Load a #UfoFilter module and return an instance. The shared object name must
+ * be * constructed as "libfilter at name.so".
+ *
+ * Since: 0.2, the error parameter is available
+ *
+ * Returns: (transfer none): (allow-none): #UfoFilter or %NULL if module cannot be found
+ */
+UfoNode *
+ufo_plugin_manager_get_task (UfoPluginManager *manager, const gchar *name, GError **error)
+{
+    g_return_val_if_fail (UFO_IS_PLUGIN_MANAGER (manager) && (name != NULL), NULL);
+    UfoPluginManagerPrivate *priv = UFO_PLUGIN_MANAGER_GET_PRIVATE (manager);
+    UfoNode *node;
+    NewFunc *func;
+    GModule *module;
+    gchar *func_name = NULL;
+    gchar *module_name = NULL;
+
+    g_return_val_if_fail (UFO_IS_PLUGIN_MANAGER (manager) && name != NULL, NULL);
+
+    if (!g_strcmp0 (name, "[dummy]"))
+        return ufo_dummy_task_new ();
+
+    func = g_hash_table_lookup (priv->new_funcs, name);
+
+    if (func == NULL) {
+        module_name = transform_string ("libufofilter%s.so", name, NULL);
+        func_name = transform_string ("ufo_%s_task_new", name, "_");
+
+        gchar *path = plugin_manager_get_path (priv, module_name);
+
+        if (path == NULL) {
+            g_set_error (error, UFO_PLUGIN_MANAGER_ERROR, UFO_PLUGIN_MANAGER_ERROR_MODULE_NOT_FOUND,
+                         "Module %s not found", module_name);
+            goto handle_error;
+        }
+
+        module = g_module_open (path, G_MODULE_BIND_LAZY);
+        g_free (path);
+
+        if (!module) {
+            g_set_error (error, UFO_PLUGIN_MANAGER_ERROR, UFO_PLUGIN_MANAGER_ERROR_MODULE_OPEN,
+                         "Module %s could not be opened: %s", module_name, g_module_error ());
+            goto handle_error;
+        }
+
+        func = g_malloc0 (sizeof (NewFunc));
+
+        if (!g_module_symbol (module, func_name, (gpointer *) func)) {
+            g_set_error (error, UFO_PLUGIN_MANAGER_ERROR, UFO_PLUGIN_MANAGER_ERROR_SYMBOL_NOT_FOUND,
+                         "%s is not exported by module %s: %s", func_name, module_name, g_module_error ());
+            g_free (func);
+
+            if (!g_module_close (module))
+                g_warning ("%s", g_module_error ());
+
+            goto handle_error;
+        }
+
+        priv->modules = g_slist_append (priv->modules, module);
+        g_hash_table_insert (priv->new_funcs, g_strdup (name), func);
+
+        g_free (func_name);
+        g_free (module_name);
+    }
+
+    node = (*func) ();
+    ufo_task_node_set_plugin_name (UFO_TASK_NODE (node), name);
+    g_message ("UfoPluginManager: Created %s-%p", name, (gpointer) node);
+
+    return node;
+
+handle_error:
+    g_free (module_name);
+    g_free (func_name);
+    return NULL;
+}
+
+/**
+ * ufo_plugin_manager_get_all_task_names:
+ * @manager: A #UfoPluginManager
+ *
+ * Return a list with potential filter names that match shared objects in all
+ * search paths.
+ *
+ * Return value: (element-type utf8) (transfer full): List of strings with filter names
+ */
+GList *
+ufo_plugin_manager_get_all_task_names (UfoPluginManager *manager)
+{
+    g_return_val_if_fail (UFO_IS_PLUGIN_MANAGER (manager), NULL);
+    UfoPluginManagerPrivate *priv = UFO_PLUGIN_MANAGER_GET_PRIVATE (manager);
+    GList *result = NULL;
+
+    GRegex *regex = g_regex_new ("libufofilter([A-Za-z]+).so", 0, 0, NULL);
+    GMatchInfo *match_info = NULL;
+
+    for (GList *path = g_list_first (priv->search_paths); path != NULL; path = g_list_next (path)) {
+        glob_t glob_vector;
+        gchar *pattern;
+
+        pattern = g_build_filename ((gchar *) path->data, "libufofilter*.so", NULL);
+        glob (pattern, GLOB_MARK | GLOB_TILDE, NULL, &glob_vector);
+        g_free (pattern);
+        gsize i = 0;
+
+        while (i < glob_vector.gl_pathc) {
+            g_regex_match (regex, glob_vector.gl_pathv[i], 0, &match_info);
+
+            if (g_match_info_matches (match_info)) {
+                gchar *word = g_match_info_fetch (match_info, 1);
+                result = g_list_append (result, word);
+            }
+
+            i++;
+        }
+    }
+
+    g_match_info_free (match_info);
+    g_regex_unref (regex);
+    return result;
+}
+
+static void
+ufo_plugin_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    switch (property_id) {
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_plugin_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    switch (property_id) {
+        case PROP_CONFIG:
+            {
+                GObject *value_object;
+
+                value_object = g_value_get_object (value);
+
+                if (value_object != NULL) {
+                    UfoConfig *config;
+
+                    config = UFO_CONFIG (value_object);
+                    copy_config_paths (UFO_PLUGIN_MANAGER_GET_PRIVATE (object), config);
+                }
+            }
+            break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_plugin_manager_constructed (GObject *object)
+{
+    UfoPluginManagerPrivate *priv = UFO_PLUGIN_MANAGER_GET_PRIVATE (object);
+
+    if (priv->search_paths == NULL) {
+        UfoConfig *config = ufo_config_new ();
+        copy_config_paths (priv, config);
+        g_object_unref (config);
+    }
+}
+
+static void
+ufo_plugin_manager_dispose (GObject *object)
+{
+    G_OBJECT_CLASS (ufo_plugin_manager_parent_class)->dispose (object);
+    g_debug ("UfoPluginManager: disposed");
+}
+
+static void
+ufo_plugin_manager_finalize (GObject *gobject)
+{
+    UfoPluginManager *manager = UFO_PLUGIN_MANAGER (gobject);
+    UfoPluginManagerPrivate *priv = UFO_PLUGIN_MANAGER_GET_PRIVATE (manager);
+
+    g_slist_foreach (priv->modules, (GFunc) g_module_close, NULL);
+    g_slist_free (priv->modules);
+
+    g_list_foreach (priv->search_paths, (GFunc) g_free, NULL);
+    g_list_free (priv->search_paths);
+
+    g_hash_table_destroy (priv->new_funcs);
+    G_OBJECT_CLASS (ufo_plugin_manager_parent_class)->finalize (gobject);
+    g_debug ("UfoPluginManager: finalized");
+}
+
+static void
+ufo_plugin_manager_class_init (UfoPluginManagerClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    gobject_class->get_property = ufo_plugin_manager_get_property;
+    gobject_class->set_property = ufo_plugin_manager_set_property;
+    gobject_class->constructed  = ufo_plugin_manager_constructed;
+    gobject_class->dispose      = ufo_plugin_manager_dispose;
+    gobject_class->finalize     = ufo_plugin_manager_finalize;
+
+    g_object_class_override_property (gobject_class, PROP_CONFIG, "config");
+
+    g_type_class_add_private (klass, sizeof (UfoPluginManagerPrivate));
+}
+
+static void
+ufo_plugin_manager_init (UfoPluginManager *manager)
+{
+    UfoPluginManagerPrivate *priv;
+
+    manager->priv = priv = UFO_PLUGIN_MANAGER_GET_PRIVATE (manager);
+    priv->modules = NULL;
+    priv->search_paths = NULL;
+    priv->new_funcs = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                             g_free, g_free);
+}
diff --git a/ufo/ufo-plugin-manager.h b/ufo/ufo-plugin-manager.h
new file mode 100644
index 0000000..76c6570
--- /dev/null
+++ b/ufo/ufo-plugin-manager.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_PLUGIN_MANAGER_H
+#define __UFO_PLUGIN_MANAGER_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-configurable.h>
+#include <ufo/ufo-config.h>
+#include <ufo/ufo-node.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_PLUGIN_MANAGER             (ufo_plugin_manager_get_type())
+#define UFO_PLUGIN_MANAGER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_PLUGIN_MANAGER, UfoPluginManager))
+#define UFO_IS_PLUGIN_MANAGER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_PLUGIN_MANAGER))
+#define UFO_PLUGIN_MANAGER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_PLUGIN_MANAGER, UfoPluginManagerClass))
+#define UFO_IS_PLUGIN_MANAGER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_PLUGIN_MANAGER))
+#define UFO_PLUGIN_MANAGER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_PLUGIN_MANAGER, UfoPluginManagerClass))
+
+#define UFO_PLUGIN_MANAGER_ERROR ufo_plugin_manager_error_quark()
+GQuark ufo_plugin_manager_error_quark(void);
+
+typedef enum {
+    UFO_PLUGIN_MANAGER_ERROR_MODULE_NOT_FOUND,
+    UFO_PLUGIN_MANAGER_ERROR_MODULE_OPEN,
+    UFO_PLUGIN_MANAGER_ERROR_SYMBOL_NOT_FOUND
+} UfoPluginManagerError;
+
+typedef struct _UfoPluginManager           UfoPluginManager;
+typedef struct _UfoPluginManagerClass      UfoPluginManagerClass;
+typedef struct _UfoPluginManagerPrivate    UfoPluginManagerPrivate;
+
+/**
+ * UfoPluginManager:
+ *
+ * Creates #UfoFilter instances by loading corresponding shared objects. The
+ * contents of the #UfoPluginManager structure are private and should only be
+ * accessed via the provided API.
+ */
+struct _UfoPluginManager {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoPluginManagerPrivate *priv;
+};
+
+/**
+ * UfoPluginManagerClass:
+ *
+ * #UfoPluginManager class
+ */
+struct _UfoPluginManagerClass {
+    /*< private >*/
+    GObjectClass parent_class;
+};
+
+UfoPluginManager  * ufo_plugin_manager_new                  (UfoConfig          *config);
+UfoNode           * ufo_plugin_manager_get_task             (UfoPluginManager   *manager,
+                                                             const gchar        *name,
+                                                             GError            **error);
+GList             * ufo_plugin_manager_get_all_task_names   (UfoPluginManager   *manager);
+GType               ufo_plugin_manager_get_type             (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-profiler.c b/ufo/ufo-profiler.c
new file mode 100644
index 0000000..3a9e73e
--- /dev/null
+++ b/ufo/ufo-profiler.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+#include <gmodule.h>
+#include <glob.h>
+
+#include <ufo/ufo-profiler.h>
+#include <ufo/ufo-resources.h>
+
+/**
+ * SECTION:ufo-profiler
+ * @Short_description: Profile different measures
+ * @Title: UfoProfiler
+ *
+ * The #UfoProfiler provides a drop-in replacement for a manual
+ * clEnqueueNDRangeKernel() call and tracks any associated events. The amount of
+ * profiling can be controlled with an #UfoProfilerLevel when constructing the
+ * profiler.
+ *
+ * Each #UfoFilter is assigned a profiler with ufo_profiler_set_profiler() by
+ * the managing #UfoBaseScheduler. Filter implementations should call
+ * ufo_filter_get_profiler() to receive their profiler and make profiled kernel
+ * calls with ufo_profiler_call().
+ *
+ * Moreover, a profiler object is used to measure wall clock time for I/O,
+ * synchronization and general CPU computation.
+ */
+
+G_DEFINE_TYPE(UfoProfiler, ufo_profiler, G_TYPE_OBJECT)
+
+#define UFO_PROFILER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_PROFILER, UfoProfilerPrivate))
+
+struct EventRow {
+    cl_event    event;
+    cl_kernel   kernel;
+};
+
+struct _UfoProfilerPrivate {
+    UfoProfilerLevel level;
+
+    GArray  *event_array;
+    GTimer **timers;
+};
+
+enum {
+    PROP_0,
+    N_PROPERTIES
+};
+
+/**
+ * timer_level:
+ *
+ * Maps UfoProfilerTimer to a UfoProfilerLevel
+ */
+static UfoProfilerLevel timer_level[] = {
+    UFO_PROFILER_LEVEL_IO,      /* TIMER_IO */
+    UFO_PROFILER_LEVEL_CPU,     /* TIMER_CPU */
+    UFO_PROFILER_LEVEL_SYNC,    /* TIMER_FETCH */
+    UFO_PROFILER_LEVEL_SYNC     /* TIMER_RELEASE */
+};
+
+/**
+ * UfoProfilerLevel:
+ * @UFO_PROFILER_LEVEL_NONE: Do not track any profiling information
+ * @UFO_PROFILER_LEVEL_IO: Track I/O events
+ * @UFO_PROFILER_LEVEL_OPENCL: Track OpenCL events
+ * @UFO_PROFILER_LEVEL_CPU: Track general CPU time
+ * @UFO_PROFILER_LEVEL_SYNC: Track synchronization wait time
+ *
+ * Profiling levels that the profiler supports. To set the global profiling
+ * level use the #UfoConfiguration:profile-level: property on a
+ * #UfoConfiguration object set to the #UfoScheduler.
+ */
+
+/**
+ * UfoProfilerTimer:
+ * @UFO_PROFILER_TIMER_IO: Select I/O timer
+ * @UFO_PROFILER_TIMER_CPU: Select CPU timer
+ * @UFO_PROFILER_TIMER_FETCH: Select timer that measures the synchronization
+ *  time to fetch data from the queues.
+ * @UFO_PROFILER_TIMER_RELEASE: Select timer that measures the synchronization
+ *  time to push data to the queues.
+ * @UFO_PROFILER_TIMER_CPU: Select CPU timer
+ * @UFO_PROFILER_TIMER_LAST: Auxiliary value, do not use.
+ *
+ * Use these values to select a specific timer when calling
+ * ufo_profiler_start(), ufo_profiler_stop() and ufo_profiler_elapsed().
+ */
+
+/**
+ * ufo_profiler_new:
+ * @level: Amount of information that should be tracked by the profiler.
+ *
+ * Create a profiler object.
+ *
+ * Return value: A new profiler object.
+ */
+UfoProfiler *
+ufo_profiler_new (UfoProfilerLevel level)
+{
+    UfoProfiler *profiler;
+
+    profiler = UFO_PROFILER (g_object_new (UFO_TYPE_PROFILER, NULL));
+    profiler->priv->level = level;
+    return profiler;
+}
+
+/**
+ * ufo_profiler_call:
+ * @profiler: A #UfoProfiler object.
+ * @command_queue: A %cl_command_queue
+ * @kernel: A %cl_kernel
+ * @work_dim: Number of working dimensions.
+ * @global_work_size: Sizes of global dimensions. The array must have at least
+ *      @work_dim entries.
+ * @local_work_size: Sizes of local work group dimensions. The array must have
+ *      at least @work_dim entries.
+ *
+ * Execute the @kernel using the command queue and execution parameters. The
+ * event associated with the clEnqueueNDRangeKernel() call is recorded and may
+ * be used for profiling purposes later on.
+ */
+void
+ufo_profiler_call (UfoProfiler    *profiler,
+                   gpointer        command_queue,
+                   gpointer        kernel,
+                   guint           work_dim,
+                   const gsize    *global_work_size,
+                   const gsize    *local_work_size)
+{
+    UfoProfilerPrivate *priv;
+    cl_event            event;
+    cl_event           *event_loc;
+    cl_int              cl_err;
+    struct EventRow     row;
+
+    priv = profiler->priv;
+    event_loc = priv->level & UFO_PROFILER_LEVEL_OPENCL ? &event : NULL;
+
+    cl_err = clEnqueueNDRangeKernel (command_queue,
+                                     kernel,
+                                     work_dim,
+                                     NULL,
+                                     global_work_size,
+                                     local_work_size,
+                                     0, NULL, event_loc);
+    UFO_RESOURCES_CHECK_CLERR (cl_err);
+
+    if (priv->level & UFO_PROFILER_LEVEL_OPENCL) {
+        row.event = event;
+        row.kernel = kernel;
+        g_array_append_val (priv->event_array, row);
+    }
+}
+
+/**
+ * ufo_profiler_start:
+ * @profiler: A #UfoProfiler object.
+ * @timer: Which timer to start
+ *
+ * Start @timer. The timer is not reset but accumulates the time elapsed between
+ * ufo_profiler_start() and ufo_profiler_stop() calls.
+ */
+void
+ufo_profiler_start (UfoProfiler      *profiler,
+                    UfoProfilerTimer  timer)
+{
+    g_return_if_fail (UFO_IS_PROFILER (profiler));
+
+    if (profiler->priv->level & timer_level[timer])
+        g_timer_continue (profiler->priv->timers[timer]);
+}
+
+/**
+ * ufo_profiler_stop:
+ * @profiler: A #UfoProfiler object.
+ * @timer: Which timer to stop
+ *
+ * Stop @timer. The timer is not reset but accumulates the time elapsed between
+ * ufo_profiler_start() and ufo_profiler_stop() calls.
+ */
+void
+ufo_profiler_stop (UfoProfiler       *profiler,
+                   UfoProfilerTimer   timer)
+{
+    g_return_if_fail (UFO_IS_PROFILER (profiler));
+
+    if (profiler->priv->level & timer_level[timer])
+        g_timer_stop (profiler->priv->timers[timer]);
+}
+
+/**
+ * ufo_profiler_elapsed:
+ * @profiler: A #UfoProfiler object.
+ * @timer: Which timer to start
+ *
+ * Get the elapsed time in seconds for @timer.
+ *
+ * Returns: Elapsed time in seconds.
+ */
+gdouble
+ufo_profiler_elapsed (UfoProfiler       *profiler,
+                      UfoProfilerTimer   timer)
+{
+    g_return_val_if_fail (UFO_IS_PROFILER (profiler), 0.0);
+    return g_timer_elapsed (profiler->priv->timers[timer], NULL);
+}
+
+static gchar *
+get_kernel_name (cl_kernel kernel)
+{
+    gsize size;
+    gchar *s;
+
+    clGetKernelInfo (kernel, CL_KERNEL_FUNCTION_NAME, 0, NULL, &size);
+    s = g_malloc0(size + 1);
+    clGetKernelInfo (kernel, CL_KERNEL_FUNCTION_NAME, size, s, NULL);
+    return s;
+}
+
+static void
+get_time_stamps (cl_event event, gulong *queued, gulong *submitted, gulong *start, gulong *end)
+{
+    clGetEventProfilingInfo (event, CL_PROFILING_COMMAND_QUEUED, sizeof (cl_ulong), queued, NULL);
+    clGetEventProfilingInfo (event, CL_PROFILING_COMMAND_SUBMIT, sizeof (cl_ulong), submitted, NULL);
+    clGetEventProfilingInfo (event, CL_PROFILING_COMMAND_START, sizeof (cl_ulong), start, NULL);
+    clGetEventProfilingInfo (event, CL_PROFILING_COMMAND_END, sizeof (cl_ulong), end, NULL);
+}
+
+/**
+ * ufo_profiler_foreach:
+ * @profiler: A #UfoProfiler object.
+ * @func: (scope call): The function to be called for an entry
+ * @user_data: User parameters
+ *
+ * Iterates through the recorded events and calls @func for each entry.
+ */
+void
+ufo_profiler_foreach (UfoProfiler    *profiler,
+                      UfoProfilerFunc func,
+                      gpointer        user_data)
+{
+    UfoProfilerPrivate *priv;
+    struct EventRow *row;
+
+    g_return_if_fail (UFO_IS_PROFILER (profiler));
+
+    priv = profiler->priv;
+
+    if (priv->level == UFO_PROFILER_LEVEL_NONE)
+        return;
+
+    for (guint i = 0; i < priv->event_array->len; i++) {
+        cl_command_queue queue;
+        gulong queued, submitted, start, end;
+        gchar *kernel_name;
+        gchar *row_string;
+
+        row = &g_array_index (priv->event_array, struct EventRow, i);
+
+        kernel_name = get_kernel_name (row->kernel);
+        clGetEventInfo (row->event, CL_EVENT_COMMAND_QUEUE, sizeof (cl_command_queue), &queue, NULL);
+        get_time_stamps (row->event, &queued, &submitted, &start, &end);
+
+        row_string = g_strdup_printf ("%s %p %lu %lu %lu %lu",
+                                      kernel_name,
+                                      (gpointer) queue,
+                                      queued, submitted, start, end);
+        func (row_string, user_data);
+
+        g_free (row_string);
+        g_free (kernel_name);
+    }
+}
+
+static void
+ufo_profiler_dispose (GObject *object)
+{
+    G_OBJECT_CLASS (ufo_profiler_parent_class)->dispose (object);
+    g_message ("UfoProfiler: disposed");
+}
+
+static void
+ufo_profiler_finalize (GObject *object)
+{
+    UfoProfilerPrivate *priv;
+
+    G_OBJECT_CLASS (ufo_profiler_parent_class)->finalize (object);
+    priv = UFO_PROFILER_GET_PRIVATE (object);
+
+    g_array_free (priv->event_array, TRUE);
+
+    for (guint i = 0; i < UFO_PROFILER_TIMER_LAST; i++)
+        g_timer_destroy (priv->timers[i]);
+
+    g_free (priv->timers);
+
+    g_message ("UfoProfiler: finalized");
+}
+
+static void
+ufo_profiler_class_init (UfoProfilerClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    gobject_class->dispose = ufo_profiler_dispose;
+    gobject_class->finalize = ufo_profiler_finalize;
+
+    g_type_class_add_private (klass, sizeof (UfoProfilerPrivate));
+}
+
+static void
+ufo_profiler_init (UfoProfiler *manager)
+{
+    UfoProfilerPrivate *priv;
+
+    manager->priv = priv = UFO_PROFILER_GET_PRIVATE (manager);
+    priv->event_array = g_array_sized_new (FALSE, TRUE, sizeof(struct EventRow), 2048);
+
+    /* Setup timers for all events */
+    priv->timers = g_new0 (GTimer *, UFO_PROFILER_TIMER_LAST);
+
+    for (guint i = 0; i < UFO_PROFILER_TIMER_LAST; i++) {
+        GTimer *timer;
+
+        timer = g_timer_new ();
+        g_timer_stop (timer);
+        g_timer_reset (timer);
+        priv->timers[i] = timer;
+    }
+
+    priv->level = UFO_PROFILER_LEVEL_NONE;
+}
diff --git a/ufo/ufo-profiler.h b/ufo/ufo-profiler.h
new file mode 100644
index 0000000..050e251
--- /dev/null
+++ b/ufo/ufo-profiler.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_PROFILER_H
+#define __UFO_PROFILER_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_PROFILER             (ufo_profiler_get_type())
+#define UFO_PROFILER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_PROFILER, UfoProfiler))
+#define UFO_IS_PROFILER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_PROFILER))
+#define UFO_PROFILER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_PROFILER, UfoProfilerClass))
+#define UFO_IS_PROFILER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_PROFILER))
+#define UFO_PROFILER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_PROFILER, UfoProfilerClass))
+
+typedef struct _UfoProfiler           UfoProfiler;
+typedef struct _UfoProfilerClass      UfoProfilerClass;
+typedef struct _UfoProfilerPrivate    UfoProfilerPrivate;
+
+/**
+ * UfoProfiler:
+ *
+ * The #UfoProfiler collects and records OpenCL events and stores them in a
+ * convenient format on disk or prints summaries on screen.
+ */
+struct _UfoProfiler {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoProfilerPrivate *priv;
+};
+
+/**
+ * UfoProfilerFunc:
+ * @row: A string with profiling information for a certain event.
+ * @user_data: User data passed to ufo_profiler_foreach().
+ *
+ * Specifies the type of functions passed to ufo_profiler_foreach().
+ */
+typedef void (*UfoProfilerFunc) (const gchar *row, gpointer user_data);
+
+/**
+ * UfoProfilerClass:
+ *
+ * #UfoProfiler class
+ */
+struct _UfoProfilerClass {
+    /*< private >*/
+    GObjectClass parent_class;
+};
+
+typedef enum {
+    UFO_PROFILER_TIMER_IO = 0,
+    UFO_PROFILER_TIMER_CPU,
+    UFO_PROFILER_TIMER_FETCH,
+    UFO_PROFILER_TIMER_RELEASE,
+    UFO_PROFILER_TIMER_LAST,
+} UfoProfilerTimer;
+
+typedef enum {
+    UFO_PROFILER_LEVEL_NONE     = 0,
+    UFO_PROFILER_LEVEL_CPU      = 1 << 0,
+    UFO_PROFILER_LEVEL_OPENCL   = 1 << 1,
+    UFO_PROFILER_LEVEL_IO       = 1 << 2,
+    UFO_PROFILER_LEVEL_SYNC     = 1 << 3
+} UfoProfilerLevel;
+
+UfoProfiler *ufo_profiler_new       (UfoProfilerLevel    level);
+void         ufo_profiler_call      (UfoProfiler        *profiler,
+                                     gpointer            command_queue,
+                                     gpointer            kernel,
+                                     guint               work_dim,
+                                     const gsize        *global_work_size,
+                                     const gsize        *local_work_size);
+void         ufo_profiler_foreach   (UfoProfiler        *profiler,
+                                     UfoProfilerFunc     func,
+                                     gpointer            user_data);
+void         ufo_profiler_start     (UfoProfiler        *profiler,
+                                     UfoProfilerTimer    timer);
+void         ufo_profiler_stop      (UfoProfiler        *profiler,
+                                     UfoProfilerTimer    timer);
+gdouble      ufo_profiler_elapsed   (UfoProfiler        *profiler,
+                                     UfoProfilerTimer    timer);
+GType        ufo_profiler_get_type  (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-remote-node.c b/ufo/ufo-remote-node.c
new file mode 100644
index 0000000..21d367a
--- /dev/null
+++ b/ufo/ufo-remote-node.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <zmq.h>
+#include <string.h>
+#include <ufo/ufo-remote-node.h>
+
+G_DEFINE_TYPE (UfoRemoteNode, ufo_remote_node, UFO_TYPE_NODE)
+
+#define UFO_REMOTE_NODE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_REMOTE_NODE, UfoRemoteNodePrivate))
+
+static void ufo_msg_send (UfoMessage *msg, gpointer socket, gint flags);
+static void receive_ack (gpointer socket);
+
+struct _UfoRemoteNodePrivate {
+    gpointer context;
+    gpointer socket;
+    guint n_inputs;
+    GMutex *mutex;
+};
+
+UfoNode *
+ufo_remote_node_new (gpointer zmq_context,
+                     const gchar *address)
+{
+    UfoRemoteNode *node;
+    UfoRemoteNodePrivate *priv;
+
+    g_return_val_if_fail (zmq_context != NULL && address != NULL, NULL);
+    node = UFO_REMOTE_NODE (g_object_new (UFO_TYPE_REMOTE_NODE, NULL));
+    priv = node->priv;
+    priv->context = zmq_context;
+    priv->socket = zmq_socket (zmq_context, ZMQ_REQ);
+
+    if (zmq_connect (priv->socket, address) == 0) {
+        g_message ("Connected remote node to `%s' via socket=%p",
+                   address,
+                   priv->socket);
+        return UFO_NODE (node);
+    }
+    else {
+        g_warning ("Could not connect to `%s': %s",
+                   address,
+                   zmq_strerror (errno));
+        g_object_unref (node);
+        return NULL;
+    }
+}
+
+guint
+ufo_remote_node_get_num_gpus (UfoRemoteNode *node)
+{
+    UfoRemoteNodePrivate *priv;
+    UfoMessage request;
+    UfoMessage result;
+    zmq_msg_t reply;
+
+    g_return_val_if_fail (UFO_IS_REMOTE_NODE (node), 0);
+    priv = node->priv;
+
+    g_mutex_lock (priv->mutex);
+
+    request.type = UFO_MESSAGE_GET_NUM_DEVICES;
+    ufo_msg_send (&request, priv->socket, 0);
+
+    zmq_msg_init (&reply);
+    zmq_msg_recv (&reply, priv->socket, 0);
+    memcpy (&result, zmq_msg_data (&reply), sizeof (UfoMessage));
+    zmq_msg_close (&reply);
+
+    g_mutex_unlock (priv->mutex);
+
+    return result.d.n_devices;
+}
+
+void
+ufo_remote_node_request_setup (UfoRemoteNode *node)
+{
+    UfoRemoteNodePrivate *priv;
+    UfoMessage request;
+
+    g_return_if_fail (UFO_IS_REMOTE_NODE (node));
+
+    priv = node->priv;
+    request.type = UFO_MESSAGE_SETUP;
+
+    g_mutex_lock (priv->mutex);
+
+    ufo_msg_send (&request, priv->socket, 0);
+    receive_ack (priv->socket);
+
+    g_mutex_unlock (priv->mutex);
+}
+
+void
+ufo_remote_node_send_json (UfoRemoteNode *node,
+                           const gchar *json,
+                           gsize size)
+{
+    UfoRemoteNodePrivate *priv;
+    UfoMessage request;
+    zmq_msg_t json_msg;
+
+    g_return_if_fail (UFO_IS_REMOTE_NODE (node));
+
+    priv = node->priv;
+    request.type = UFO_MESSAGE_TASK_JSON;
+
+    g_mutex_lock (priv->mutex);
+
+    ufo_msg_send (&request, priv->socket, ZMQ_SNDMORE);
+
+    zmq_msg_init_size (&json_msg, size);
+    memcpy (zmq_msg_data (&json_msg), json, size);
+    zmq_msg_send (&json_msg, priv->socket, 0);
+    zmq_msg_close (&json_msg);
+
+    receive_ack (priv->socket);
+
+    g_mutex_unlock (priv->mutex);
+}
+
+void
+ufo_remote_node_get_structure (UfoRemoteNode *node,
+                               guint *n_inputs,
+                               UfoInputParam **in_params,
+                               UfoTaskMode *mode)
+{
+    UfoRemoteNodePrivate *priv;
+    UfoMessage request;
+    UfoMessage *header;
+    zmq_msg_t header_msg;
+    zmq_msg_t payload_msg;
+    UfoInputParam *in_param;
+
+    g_return_if_fail (UFO_IS_REMOTE_NODE (node));
+
+    priv = node->priv;
+    *mode = UFO_TASK_MODE_SINGLE;
+    request.type = UFO_MESSAGE_GET_STRUCTURE;
+
+    g_mutex_lock (priv->mutex);
+
+    ufo_msg_send (&request, priv->socket, 0);
+
+    /* Receive header */
+    zmq_msg_init (&header_msg);
+    zmq_msg_recv (&header_msg, priv->socket, 0);
+    header = (UfoMessage *) zmq_msg_data (&header_msg);
+
+    /* Receive payload */
+    zmq_msg_init (&payload_msg);
+    zmq_msg_recv (&payload_msg, priv->socket, 0);
+    in_param = (UfoInputParam *) zmq_msg_data (&payload_msg);
+
+    priv->n_inputs = header->d.n_inputs;
+    *n_inputs = header->d.n_inputs;
+    *in_params = g_new0 (UfoInputParam, 1);
+    (*in_params)[0].n_dims = in_param->n_dims;
+
+    zmq_msg_close (&header_msg);
+    zmq_msg_close (&payload_msg);
+
+    g_mutex_unlock (priv->mutex);
+}
+
+void
+ufo_remote_node_send_inputs (UfoRemoteNode *node,
+                             UfoBuffer **inputs)
+{
+    UfoRemoteNodePrivate *priv;
+    UfoMessage request;
+
+    g_return_if_fail (UFO_IS_REMOTE_NODE (node));
+
+    priv = node->priv;
+    request.type = UFO_MESSAGE_SEND_INPUTS;
+
+    g_mutex_lock (priv->mutex);
+
+    ufo_msg_send (&request, priv->socket, ZMQ_SNDMORE);
+
+    /*
+     * For each of the input data items send two frames: the first one contains
+     * the size as an UfoRequisition struct and the second one the raw byte
+     * data.
+     */
+    for (guint i = 0; i < priv->n_inputs; i++) {
+        UfoRequisition requisition;
+        zmq_msg_t requisition_msg;
+        zmq_msg_t data_msg;
+        gsize size;
+        gint flags;
+
+        ufo_buffer_get_requisition (inputs[i], &requisition);
+        size = ufo_buffer_get_size (inputs[i]);
+
+        zmq_msg_init_size (&requisition_msg, sizeof (UfoRequisition));
+        zmq_msg_init_size (&data_msg, size);
+
+        memcpy (zmq_msg_data (&requisition_msg), &requisition, sizeof (UfoRequisition));
+        memcpy (zmq_msg_data (&data_msg), ufo_buffer_get_host_array (inputs[i], NULL), size);
+
+        flags = i == priv->n_inputs - 1 ? 0 : ZMQ_SNDMORE;
+        zmq_msg_send (&requisition_msg, priv->socket, ZMQ_SNDMORE);
+        zmq_msg_send (&data_msg, priv->socket, flags);
+
+        zmq_msg_close (&requisition_msg);
+        zmq_msg_close (&data_msg);
+    }
+
+    receive_ack (priv->socket);
+    g_mutex_unlock (priv->mutex);
+}
+
+void
+ufo_remote_node_get_result (UfoRemoteNode *node,
+                            UfoBuffer *buffer)
+{
+    UfoRemoteNodePrivate *priv;
+    UfoMessage request;
+    zmq_msg_t reply_msg;
+    gpointer host_array;
+
+    g_return_if_fail (UFO_IS_REMOTE_NODE (node));
+
+    priv = node->priv;
+    request.type = UFO_MESSAGE_GET_RESULT;
+
+    g_mutex_lock (priv->mutex);
+
+    ufo_msg_send (&request, priv->socket, 0);
+
+    /* Get the remote data and put it into our buffer */
+    zmq_msg_init (&reply_msg);
+    zmq_msg_recv (&reply_msg, priv->socket, 0);
+
+    ufo_buffer_discard_location (buffer, UFO_LOCATION_DEVICE);
+    host_array = ufo_buffer_get_host_array (buffer, NULL);
+    g_assert (ufo_buffer_get_size (buffer) == zmq_msg_size (&reply_msg));
+    memcpy (host_array, zmq_msg_data (&reply_msg), ufo_buffer_get_size (buffer));
+
+    zmq_msg_close (&reply_msg);
+
+    g_mutex_unlock (priv->mutex);
+}
+
+void
+ufo_remote_node_get_requisition (UfoRemoteNode *node,
+                                 UfoRequisition *requisition)
+{
+    UfoRemoteNodePrivate *priv;
+    UfoMessage request;
+    zmq_msg_t reply_msg;
+
+    g_return_if_fail (UFO_IS_REMOTE_NODE (node));
+
+    priv = node->priv;
+    request.type = UFO_MESSAGE_GET_REQUISITION;
+
+    g_mutex_lock (priv->mutex);
+
+    ufo_msg_send (&request, priv->socket, 0);
+
+    zmq_msg_init (&reply_msg);
+    zmq_msg_recv (&reply_msg, priv->socket, 0);
+    g_assert (zmq_msg_size (&reply_msg) >= sizeof (UfoRequisition));
+    memcpy (requisition, zmq_msg_data (&reply_msg), sizeof (UfoRequisition));
+    zmq_msg_close (&reply_msg);
+
+    g_mutex_unlock (priv->mutex);
+}
+
+static void
+cleanup_remote (gpointer socket)
+{
+    UfoMessage request;
+
+    request.type = UFO_MESSAGE_CLEANUP;
+
+    ufo_msg_send (&request, socket, 0);
+    receive_ack (socket);
+}
+
+static void
+ufo_msg_send (UfoMessage *msg,
+              gpointer socket,
+              gint flags)
+{
+    zmq_msg_t request;
+
+    zmq_msg_init_size (&request, sizeof (UfoMessage));
+    memcpy (zmq_msg_data (&request), msg, sizeof (UfoMessage));
+    zmq_msg_send (&request, socket, flags);
+    zmq_msg_close (&request);
+}
+
+static void
+receive_ack (gpointer socket)
+{
+    zmq_msg_t reply_msg;
+
+    zmq_msg_init (&reply_msg);
+    zmq_msg_recv (&reply_msg, socket, 0);
+    zmq_msg_close (&reply_msg);
+}
+
+static void
+ufo_remote_node_dispose (GObject *object)
+{
+    UfoRemoteNodePrivate *priv;
+
+    priv = UFO_REMOTE_NODE_GET_PRIVATE (object);
+
+    if (priv->socket != NULL) {
+        cleanup_remote (priv->socket);
+
+        g_debug ("Close socket=%p", priv->socket);
+        zmq_close (priv->socket);
+        priv->socket = NULL;
+    }
+
+    G_OBJECT_CLASS (ufo_remote_node_parent_class)->dispose (object);
+}
+
+static void
+ufo_remote_node_finalize (GObject *object)
+{
+    UfoRemoteNodePrivate *priv;
+
+    priv = UFO_REMOTE_NODE_GET_PRIVATE (object);
+    g_mutex_free (priv->mutex);
+
+    G_OBJECT_CLASS (ufo_remote_node_parent_class)->finalize (object);
+}
+
+static void
+ufo_remote_node_class_init (UfoRemoteNodeClass *klass)
+{
+    GObjectClass *oclass;
+
+    oclass = G_OBJECT_CLASS (klass);
+    oclass->dispose = ufo_remote_node_dispose;
+    oclass->finalize = ufo_remote_node_finalize;
+
+    g_type_class_add_private (klass, sizeof(UfoRemoteNodePrivate));
+}
+
+static void
+ufo_remote_node_init (UfoRemoteNode *self)
+{
+    UfoRemoteNodePrivate *priv;
+    self->priv = priv = UFO_REMOTE_NODE_GET_PRIVATE (self);
+    priv->context = NULL;
+    priv->socket = NULL;
+    priv->n_inputs = 0;
+    priv->mutex = g_mutex_new ();
+}
diff --git a/ufo/ufo-remote-node.h b/ufo/ufo-remote-node.h
new file mode 100644
index 0000000..ad2bf63
--- /dev/null
+++ b/ufo/ufo-remote-node.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_REMOTE_NODE_H
+#define __UFO_REMOTE_NODE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-node.h>
+#include <ufo/ufo-buffer.h>
+#include <ufo/ufo-task-iface.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_REMOTE_NODE             (ufo_remote_node_get_type())
+#define UFO_REMOTE_NODE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_REMOTE_NODE, UfoRemoteNode))
+#define UFO_IS_REMOTE_NODE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_REMOTE_NODE))
+#define UFO_REMOTE_NODE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_REMOTE_NODE, UfoRemoteNodeClass))
+#define UFO_IS_REMOTE_NODE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_REMOTE_NODE))
+#define UFO_REMOTE_NODE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_REMOTE_NODE, UfoRemoteNodeClass))
+
+typedef struct _UfoRemoteNode           UfoRemoteNode;
+typedef struct _UfoRemoteNodeClass      UfoRemoteNodeClass;
+typedef struct _UfoRemoteNodePrivate    UfoRemoteNodePrivate;
+typedef struct _UfoMessage              UfoMessage;
+
+/**
+ * UfoRemoteNode:
+ *
+ * Main object for organizing filters. The contents of the #UfoRemoteNode structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoRemoteNode {
+    /*< private >*/
+    UfoNode parent_instance;
+
+    UfoRemoteNodePrivate *priv;
+};
+
+/**
+ * UfoRemoteNodeClass:
+ *
+ * #UfoRemoteNode class
+ */
+struct _UfoRemoteNodeClass {
+    /*< private >*/
+    UfoNodeClass parent_class;
+};
+
+/**
+ * UfoMessageType: (skip)
+ * @UFO_MESSAGE_TASK_JSON: insert
+ * @UFO_MESSAGE_GET_NUM_DEVICES: insert
+ * @UFO_MESSAGE_SETUP: insert
+ * @UFO_MESSAGE_GET_STRUCTURE: insert
+ * @UFO_MESSAGE_STRUCTURE: insert
+ * @UFO_MESSAGE_GET_REQUISITION: insert
+ * @UFO_MESSAGE_REQUISITION: insert
+ * @UFO_MESSAGE_SEND_INPUTS: insert
+ * @UFO_MESSAGE_GET_RESULT: insert
+ * @UFO_MESSAGE_RESULT: insert
+ * @UFO_MESSAGE_CLEANUP: insert
+ * @UFO_MESSAGE_ACK: insert
+ */
+typedef enum {
+    UFO_MESSAGE_TASK_JSON = 0,
+    UFO_MESSAGE_GET_NUM_DEVICES,
+    UFO_MESSAGE_SETUP,
+    UFO_MESSAGE_GET_STRUCTURE,
+    UFO_MESSAGE_STRUCTURE,
+    UFO_MESSAGE_GET_REQUISITION,
+    UFO_MESSAGE_REQUISITION,
+    UFO_MESSAGE_SEND_INPUTS,
+    UFO_MESSAGE_GET_RESULT,
+    UFO_MESSAGE_RESULT,
+    UFO_MESSAGE_CLEANUP,
+    UFO_MESSAGE_ACK
+} UfoMessageType;
+
+/**
+ * UfoMessage: (skip)
+ * @type: Type of the wire message
+ */
+struct _UfoMessage {
+    UfoMessageType  type;
+
+    union {
+        guint16 n_inputs;
+        guint16 n_devices;
+    } d;
+};
+
+UfoNode  *ufo_remote_node_new               (gpointer     zmq_context,
+                                             const gchar    *address);
+guint     ufo_remote_node_get_num_gpus      (UfoRemoteNode  *node);
+void      ufo_remote_node_request_setup     (UfoRemoteNode  *node);
+void      ufo_remote_node_send_json         (UfoRemoteNode  *node,
+                                             const gchar    *json,
+                                             gsize           size);
+void      ufo_remote_node_get_structure     (UfoRemoteNode  *node,
+                                             guint          *n_inputs,
+                                             UfoInputParam **in_params,
+                                             UfoTaskMode    *mode);
+void      ufo_remote_node_send_inputs       (UfoRemoteNode  *node,
+                                             UfoBuffer     **inputs);
+void      ufo_remote_node_get_result        (UfoRemoteNode  *node,
+                                             UfoBuffer      *result);
+void      ufo_remote_node_get_requisition   (UfoRemoteNode  *node,
+                                             UfoRequisition *requisition);
+void      ufo_remote_node_cleanup           (UfoRemoteNode  *node);
+GType     ufo_remote_node_get_type          (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-remote-task.c b/ufo/ufo-remote-task.c
new file mode 100644
index 0000000..5aa54b9
--- /dev/null
+++ b/ufo/ufo-remote-task.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gmodule.h>
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#include <ufo/ufo-remote-task.h>
+#include <ufo/ufo-remote-node.h>
+#include <ufo/ufo-cpu-task-iface.h>
+
+/**
+ * SECTION:ufo-remote-task
+ * @Short_description: Encapsulate remote tasks
+ * @Title: UfoRemoteTask
+ */
+
+struct _UfoRemoteTaskPrivate {
+    UfoRemoteNode *remote;
+};
+
+static void ufo_task_interface_init (UfoTaskIface *iface);
+static void ufo_cpu_task_interface_init (UfoCpuTaskIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (UfoRemoteTask, ufo_remote_task, UFO_TYPE_TASK_NODE,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_TASK,
+                                                ufo_task_interface_init)
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_CPU_TASK,
+                                                ufo_cpu_task_interface_init))
+
+#define UFO_REMOTE_TASK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_REMOTE_TASK, UfoRemoteTaskPrivate))
+
+enum {
+    PROP_0,
+    N_PROPERTIES
+};
+
+UfoNode *
+ufo_remote_task_new (void)
+{
+    return UFO_NODE (g_object_new (UFO_TYPE_REMOTE_TASK, NULL));
+}
+
+static void
+ufo_remote_task_setup (UfoTask *task,
+                       UfoResources *resources,
+                       GError **error)
+{
+    UfoRemoteTaskPrivate *priv;
+
+    priv = UFO_REMOTE_TASK_GET_PRIVATE (UFO_REMOTE_TASK (task));
+    priv->remote = UFO_REMOTE_NODE (ufo_task_node_get_proc_node (UFO_TASK_NODE (task)));
+    g_assert (priv->remote != NULL);
+    ufo_remote_node_get_num_gpus (priv->remote);
+    ufo_remote_node_request_setup (priv->remote);
+}
+
+static void
+ufo_remote_task_get_requisition (UfoTask *task,
+                                 UfoBuffer **inputs,
+                                 UfoRequisition *requisition)
+{
+    UfoRemoteTaskPrivate *priv;
+
+    priv = UFO_REMOTE_TASK_GET_PRIVATE (UFO_REMOTE_TASK (task));
+
+    /*
+     * We send our input to the remote node which will execute immediately.
+     * After remote execution, we will know the requisition of the _last_ remote
+     * task node and can get it back.
+     */
+    ufo_remote_node_send_inputs (priv->remote, inputs);
+    ufo_remote_node_get_requisition (priv->remote, requisition);
+}
+
+static void
+ufo_remote_task_get_structure (UfoTask *task,
+                               guint *n_inputs,
+                               UfoInputParam **in_params,
+                               UfoTaskMode *mode)
+{
+    UfoRemoteTaskPrivate *priv;
+
+    priv = UFO_REMOTE_TASK_GET_PRIVATE (UFO_REMOTE_TASK (task));
+    ufo_remote_node_get_structure (priv->remote, n_inputs, in_params, mode);
+}
+
+static gboolean
+ufo_remote_task_process (UfoCpuTask *task,
+                         UfoBuffer **inputs,
+                         UfoBuffer *output,
+                         UfoRequisition *requisition)
+{
+    UfoRemoteTaskPrivate *priv;
+    priv = UFO_REMOTE_TASK_GET_PRIVATE (UFO_REMOTE_TASK (task));
+
+    ufo_remote_node_get_result (priv->remote, output);
+    return TRUE;
+}
+
+static void
+ufo_remote_task_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    switch (property_id) {
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_remote_task_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    switch (property_id) {
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_remote_task_dispose (GObject *object)
+{
+    G_OBJECT_CLASS (ufo_remote_task_parent_class)->dispose (object);
+}
+
+static void
+ufo_task_interface_init (UfoTaskIface *iface)
+{
+    iface->setup = ufo_remote_task_setup;
+    iface->get_structure = ufo_remote_task_get_structure;
+    iface->get_requisition = ufo_remote_task_get_requisition;
+}
+
+static void
+ufo_cpu_task_interface_init (UfoCpuTaskIface *iface)
+{
+    iface->process = ufo_remote_task_process;
+}
+
+static void
+ufo_remote_task_class_init (UfoRemoteTaskClass *klass)
+{
+    GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+    oclass->set_property = ufo_remote_task_set_property;
+    oclass->get_property = ufo_remote_task_get_property;
+    oclass->dispose = ufo_remote_task_dispose;
+
+    g_type_class_add_private (oclass, sizeof(UfoRemoteTaskPrivate));
+}
+
+static void
+ufo_remote_task_init(UfoRemoteTask *self)
+{
+    self->priv = UFO_REMOTE_TASK_GET_PRIVATE(self);
+}
diff --git a/ufo/ufo-remote-task.h b/ufo/ufo-remote-task.h
new file mode 100644
index 0000000..3fed504
--- /dev/null
+++ b/ufo/ufo-remote-task.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_REMOTE_TASK_H
+#define __UFO_REMOTE_TASK_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-task-node.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_REMOTE_TASK             (ufo_remote_task_get_type())
+#define UFO_REMOTE_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_REMOTE_TASK, UfoRemoteTask))
+#define UFO_IS_REMOTE_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_REMOTE_TASK))
+#define UFO_REMOTE_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_REMOTE_TASK, UfoRemoteTaskClass))
+#define UFO_IS_REMOTE_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_REMOTE_TASK))
+#define UFO_REMOTE_TASK_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_REMOTE_TASK, UfoRemoteTaskClass))
+
+typedef struct _UfoRemoteTask           UfoRemoteTask;
+typedef struct _UfoRemoteTaskClass      UfoRemoteTaskClass;
+typedef struct _UfoRemoteTaskPrivate    UfoRemoteTaskPrivate;
+
+/**
+ * UfoRemoteTask:
+ *
+ * Main object for organizing filters. The contents of the #UfoRemoteTask structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoRemoteTask {
+    /*< private >*/
+    UfoTaskNode parent_instance;
+
+    UfoRemoteTaskPrivate *priv;
+};
+
+/**
+ * UfoRemoteTaskClass:
+ *
+ * #UfoRemoteTask class
+ */
+struct _UfoRemoteTaskClass {
+    /*< private >*/
+    UfoTaskNodeClass parent_class;
+};
+
+UfoNode  *ufo_remote_task_new       (void);
+GType     ufo_remote_task_get_type  (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-resources.c b/ufo/ufo-resources.c
new file mode 100644
index 0000000..d8295e7
--- /dev/null
+++ b/ufo/ufo-resources.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <stdio.h>
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#include <ufo/ufo-resources.h>
+#include <ufo/ufo-configurable.h>
+
+/**
+ * SECTION:ufo-resources
+ * @Short_description: Manage OpenCL resources
+ * @Title: UfoResources
+ *
+ * The #UfoResources creates the OpenCL environment and loads OpenCL
+ * kernels from text files.
+ */
+
+G_DEFINE_TYPE_WITH_CODE (UfoResources, ufo_resources, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_CONFIGURABLE, NULL))
+
+#define UFO_RESOURCES_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_RESOURCES, UfoResourcesPrivate))
+
+/**
+ * UfoResourcesError:
+ * @UFO_RESOURCES_ERROR_LOAD_PROGRAM: Could not load the OpenCL file
+ * @UFO_RESOURCES_ERROR_CREATE_PROGRAM: Could not create a program from
+ *      the sources
+ * @UFO_RESOURCES_ERROR_BUILD_PROGRAM: Could not build program from
+ *      sources
+ * @UFO_RESOURCES_ERROR_CREATE_KERNEL: Could not create kernel
+ *
+ * OpenCL related errors.
+ */
+GQuark
+ufo_resources_error_quark (void)
+{
+    return g_quark_from_static_string ("ufo-resource-resources-error-quark");
+}
+
+struct _UfoResourcesPrivate {
+    UfoConfig       *config;
+
+    cl_uint          num_platforms;
+    cl_platform_id  *opencl_platforms;
+    cl_context       opencl_context;
+    cl_uint         *num_devices;       /**< Number of OpenCL devices per platform id */
+    cl_device_id   **opencl_devices;    /**< Array of OpenCL devices per platform id */
+    cl_command_queue *command_queues;    /**< Array of command queues per device */
+
+    GList       *kernel_paths;          /**< Colon-separated string with paths to kernel files */
+    GHashTable  *opencl_programs;       /**< Map from filename to cl_program */
+    GList       *opencl_kernels;
+    GString     *opencl_build_options;
+    GString     *include_paths;         /**< List of include paths "-I/foo/bar" built from added paths */
+};
+
+enum {
+    PROP_0,
+    PROP_CONFIG,
+    N_PROPERTIES
+};
+
+const gchar *opencl_error_msgs[] = {
+    "CL_SUCCESS",
+    "CL_DEVICE_NOT_FOUND",
+    "CL_DEVICE_NOT_AVAILABLE",
+    "CL_COMPILER_NOT_AVAILABLE",
+    "CL_MEM_OBJECT_ALLOCATION_FAILURE",
+    "CL_OUT_OF_RESOURCES",
+    "CL_OUT_OF_HOST_MEMORY",
+    "CL_PROFILING_INFO_NOT_AVAILABLE",
+    "CL_MEM_COPY_OVERLAP",
+    "CL_IMAGE_FORMAT_MISMATCH",
+    "CL_IMAGE_FORMAT_NOT_SUPPORTED",
+    "CL_BUILD_PROGRAM_FAILURE",
+    "CL_MAP_FAILURE",
+    "CL_MISALIGNED_SUB_BUFFER_OFFSET",
+    "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST",
+
+    /* next IDs start at 30! */
+    "CL_INVALID_VALUE",
+    "CL_INVALID_DEVICE_TYPE",
+    "CL_INVALID_PLATFORM",
+    "CL_INVALID_DEVICE",
+    "CL_INVALID_CONTEXT",
+    "CL_INVALID_QUEUE_PROPERTIES",
+    "CL_INVALID_COMMAND_QUEUE",
+    "CL_INVALID_HOST_PTR",
+    "CL_INVALID_MEM_OBJECT",
+    "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR",
+    "CL_INVALID_IMAGE_SIZE",
+    "CL_INVALID_SAMPLER",
+    "CL_INVALID_BINARY",
+    "CL_INVALID_BUILD_OPTIONS",
+    "CL_INVALID_PROGRAM",
+    "CL_INVALID_PROGRAM_EXECUTABLE",
+    "CL_INVALID_KERNEL_NAME",
+    "CL_INVALID_KERNEL_DEFINITION",
+    "CL_INVALID_KERNEL",
+    "CL_INVALID_ARG_INDEX",
+    "CL_INVALID_ARG_VALUE",
+    "CL_INVALID_ARG_SIZE",
+    "CL_INVALID_KERNEL_ARGS",
+    "CL_INVALID_WORK_DIMENSION",
+    "CL_INVALID_WORK_GROUP_SIZE",
+    "CL_INVALID_WORK_ITEM_SIZE",
+    "CL_INVALID_GLOBAL_OFFSET",
+    "CL_INVALID_EVENT_WAIT_LIST",
+    "CL_INVALID_EVENT",
+    "CL_INVALID_OPERATION",
+    "CL_INVALID_GL_OBJECT",
+    "CL_INVALID_BUFFER_SIZE",
+    "CL_INVALID_MIP_LEVEL",
+    "CL_INVALID_GLOBAL_WORK_SIZE"
+};
+
+/**
+ * ufo_resources_clerr:
+ * @error: An OpenCL error code
+ *
+ * Get a human-readable string representation of @error.
+ *
+ * Returns: (transfer none): A static string of @error.
+ */
+const gchar *
+ufo_resources_clerr (int error)
+{
+    static const gchar *invalid = "Invalid error code";
+    const gint array_size = sizeof opencl_error_msgs/sizeof(gchar*);
+    gint index;
+
+    index = error >= -14 ? -error : -error-15;
+
+    if (index >= 0 && index < array_size)
+        return opencl_error_msgs[index];
+
+    return invalid;
+}
+
+static gchar *
+resources_load_opencl_program (const gchar *filename)
+{
+    FILE *fp = fopen (filename, "r");
+
+    if (fp == NULL)
+        return NULL;
+
+    fseek (fp, 0, SEEK_END);
+    const gsize length = (gsize) ftell (fp);
+    rewind (fp);
+    gchar *buffer = (gchar *) g_malloc0(length + 1);
+
+    if (buffer == NULL) {
+        fclose (fp);
+        return NULL;
+    }
+
+    size_t buffer_length = fread (buffer, 1, length, fp);
+    fclose (fp);
+
+    if (buffer_length != length) {
+        g_free (buffer);
+        return NULL;
+    }
+
+    return buffer;
+}
+
+static void
+resources_release_kernel (cl_kernel kernel)
+{
+    UFO_RESOURCES_CHECK_CLERR (clReleaseKernel (kernel));
+}
+
+static void
+resources_release_program (cl_program program)
+{
+    UFO_RESOURCES_CHECK_CLERR (clReleaseProgram (program));
+}
+
+static gchar *
+resources_find_path (UfoResourcesPrivate *priv, const gchar *filename)
+{
+    /* Check first if filename is already a path */
+    if (g_path_is_absolute (filename)) {
+        if (g_file_test (filename, G_FILE_TEST_EXISTS))
+            return g_strdup (filename);
+        else
+            return NULL;
+    }
+
+    /* If it is not a path, search in all paths that were added */
+    GList *elem = g_list_first (priv->kernel_paths);
+
+    while (elem != NULL) {
+        gchar *path = g_strdup_printf ("%s%c%s", (gchar *) elem->data, G_DIR_SEPARATOR, filename);
+
+        if (g_file_test (path, G_FILE_TEST_EXISTS))
+            return path;
+
+        g_free (path);
+        elem = g_list_next (elem);
+    }
+
+    return NULL;
+}
+
+/**
+ * ufo_resources_new:
+ * @config: A #UfoConfiguration object or %NULL
+ *
+ * Create a new #UfoResources instance.
+ *
+ * Returns: (transfer none): A new #UfoResources
+ */
+UfoResources *
+ufo_resources_new (UfoConfig *config)
+{
+    return UFO_RESOURCES (g_object_new (UFO_TYPE_RESOURCES,
+                                        "config", config,
+                                        NULL));
+}
+
+static void
+append_include_path (gchar *path,
+                     GString *directive)
+{
+    g_string_append_printf (directive, " -I%s", path);
+}
+
+static cl_program
+add_program_from_source (UfoResourcesPrivate *priv,
+                         const gchar *source,
+                         const gchar *options,
+                         GError **error)
+{
+    cl_program program;
+    gchar *build_options = NULL;
+    cl_int errcode = CL_SUCCESS;
+
+    program = clCreateProgramWithSource (priv->opencl_context,
+                                         1, &source, NULL, &errcode);
+
+    if (errcode != CL_SUCCESS) {
+        g_set_error (error,
+                     UFO_RESOURCES_ERROR,
+                     UFO_RESOURCES_ERROR_CREATE_PROGRAM,
+                     "Failed to create OpenCL program: %s", ufo_resources_clerr (errcode));
+        return NULL;
+    }
+
+    if (options != NULL)
+        build_options = g_strdup_printf ("%s %s %s", priv->opencl_build_options->str, priv->include_paths->str, options);
+    else
+        build_options = g_strdup_printf ("%s %s", priv->opencl_build_options->str, priv->include_paths->str);
+
+    errcode = clBuildProgram (program,
+                              priv->num_devices[0],
+                              priv->opencl_devices[0],
+                              build_options,
+                              NULL, NULL);
+
+    g_free (build_options);
+
+    if (errcode != CL_SUCCESS) {
+        g_set_error (error,
+                     UFO_RESOURCES_ERROR,
+                     UFO_RESOURCES_ERROR_BUILD_PROGRAM,
+                     "Failed to build OpenCL program: %s", ufo_resources_clerr (errcode));
+
+        const gsize LOG_SIZE = 4096;
+        gchar *log = (gchar *) g_malloc0(LOG_SIZE * sizeof (char));
+        UFO_RESOURCES_CHECK_CLERR (clGetProgramBuildInfo (program,
+                                                   priv->opencl_devices[0][0],
+                                                   CL_PROGRAM_BUILD_LOG,
+                                                   LOG_SIZE, (void *) log, NULL));
+        g_print ("\n=== Build log for s===%s\n\n", log);
+        g_free (log);
+        return NULL;
+    }
+
+    return program;
+}
+
+static cl_program
+resources_add_program (UfoResources *resources, const gchar *filename, const gchar *options, GError **error)
+{
+    g_return_val_if_fail (UFO_IS_RESOURCES (resources) || (filename != NULL), FALSE);
+    UfoResourcesPrivate *priv = resources->priv;
+
+    /* Programs might be added multiple times if this is not locked */
+    static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+    g_static_mutex_lock (&mutex);
+
+    /* Don't process the kernel file again, if already load */
+    cl_program program = g_hash_table_lookup (priv->opencl_programs, filename);
+
+    if (program != NULL) {
+        g_static_mutex_unlock (&mutex);
+        return program;
+    }
+
+    gchar *path = resources_find_path (priv, filename);
+
+    if (path == NULL) {
+        g_set_error (error,
+                     UFO_RESOURCES_ERROR,
+                     UFO_RESOURCES_ERROR_LOAD_PROGRAM,
+                     "Could not find `%s'. Maybe you forgot to pass a configuration?", filename);
+        g_static_mutex_unlock (&mutex);
+        return NULL;
+    }
+
+    gchar *buffer = resources_load_opencl_program (path);
+    g_free (path);
+
+    if (buffer == NULL) {
+        g_set_error (error,
+                     UFO_RESOURCES_ERROR,
+                     UFO_RESOURCES_ERROR_LOAD_PROGRAM,
+                     "Could not open `%s'", filename);
+        g_static_mutex_unlock (&mutex);
+        return NULL;
+    }
+
+    program = add_program_from_source (priv, buffer, options, error);
+    g_message ("Added program %p from `%s`", (gpointer) program, filename);
+
+    if (program != NULL)
+        g_hash_table_insert (priv->opencl_programs, g_strdup (filename), program);
+
+    g_static_mutex_unlock (&mutex);
+    g_free (buffer);
+    return program;
+}
+
+static cl_kernel
+resources_get_kernel (UfoResourcesPrivate *priv,
+                      cl_program program,
+                      const gchar *kernel_name,
+                      GError **error)
+{
+    cl_int errcode = CL_SUCCESS;
+    cl_kernel kernel = clCreateKernel (program, kernel_name, &errcode);
+
+    if (kernel == NULL || errcode != CL_SUCCESS) {
+        g_set_error (error,
+                     UFO_RESOURCES_ERROR,
+                     UFO_RESOURCES_ERROR_CREATE_KERNEL,
+                     "Failed to create kernel `%s`: %s", kernel_name, ufo_resources_clerr (errcode));
+        return NULL;
+    }
+
+    priv->opencl_kernels = g_list_append (priv->opencl_kernels, kernel);
+    return kernel;
+}
+
+/**
+ * ufo_resources_get_kernel:
+ * @resources: A #UfoResources object
+ * @filename: Name of the .cl kernel file
+ * @kernel: Name of a kernel
+ * @error: Return location for a GError from #UfoResourcesError, or NULL
+ *
+ * Loads a and builds a kernel from a file. The file is searched in the current
+ * working directory and all paths added through
+ * ufo_resources_add_paths ().
+ *
+ * Returns: (transfer none): a cl_kernel object that is load from @filename or %NULL on error
+ */
+gpointer
+ufo_resources_get_kernel (UfoResources *resources,
+                          const gchar *filename,
+                          const gchar *kernel,
+                          GError **error)
+{
+    UfoResourcesPrivate *priv;
+    cl_program program;
+    GError *tmp_error = NULL;
+
+    g_return_val_if_fail (UFO_IS_RESOURCES (resources) &&
+                          (filename != NULL) &&
+                          (kernel != NULL), NULL);
+
+    priv = resources->priv;
+    program = resources_add_program (resources, filename, "", &tmp_error);
+
+    if (program == NULL) {
+        g_propagate_error (error, tmp_error);
+        return NULL;
+    }
+
+    return resources_get_kernel (priv, program, kernel, error);
+}
+
+/**
+ * ufo_resources_get_kernel_from_source:
+ * @resources: A #UfoResources
+ * @source: OpenCL source string
+ * @kernel: Name of a kernel
+ * @error: Return location for a GError from #UfoResourcesError, or NULL
+ *
+ * Loads and builds a kernel from a string.
+ *
+ * Returns: (transfer none): a cl_kernel object that is load from @filename
+ */
+gpointer
+ufo_resources_get_kernel_from_source (UfoResources *resources,
+                                      const gchar *source,
+                                      const gchar *kernel,
+                                      GError **error)
+{
+    UfoResourcesPrivate *priv;
+    cl_program program;
+
+    g_return_val_if_fail (UFO_IS_RESOURCES (resources) &&
+                          (source != NULL) &&
+                          (kernel != NULL), NULL);
+
+    priv = UFO_RESOURCES_GET_PRIVATE (resources);
+    program = add_program_from_source (priv, source, NULL, error);
+
+    /*
+     * We add the program under a fake file name. This looks very brittle to me
+     * (kernel name could be the same as a source filename) but it should work
+     * in most cases.
+     */
+    if (program != NULL)
+        g_hash_table_insert (priv->opencl_programs, g_strdup (kernel), program);
+    else
+        return NULL;
+
+    return resources_get_kernel (priv, program, kernel, error);
+}
+
+/**
+ * ufo_resources_get_context: (skip)
+ * @resources: A #UfoResources
+ *
+ * Returns the OpenCL context object that is used by the resource resources. This
+ * context can be used to initialize othe third-party libraries.
+ *
+ * Return value: A cl_context object.
+ */
+gpointer
+ufo_resources_get_context (UfoResources *resources)
+{
+    g_return_val_if_fail (UFO_IS_RESOURCES (resources), NULL);
+    return resources->priv->opencl_context;
+}
+
+/**
+ * ufo_resources_get_cmd_queues:
+ * @resources: A #UfoResources
+ *
+ * Get all command queues managed by @resources.
+ *
+ * Return value: (element-type gpointer) (transfer container): List with
+ * cl_command_queue objects. Free with g_list_free() but not its elements.
+ */
+GList *
+ufo_resources_get_cmd_queues (UfoResources *resources)
+{
+    UfoResourcesPrivate *priv;
+    GList *result = NULL;
+
+    g_return_val_if_fail (UFO_IS_RESOURCES (resources), NULL);
+    priv = resources->priv;
+
+    for (guint i = 0; i < priv->num_devices[0]; i++)
+        result = g_list_append (result, priv->command_queues[i]);
+
+    return result;
+}
+
+static void
+ufo_resources_set_property (GObject *object,
+                            guint property_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+    UfoResourcesPrivate *priv = UFO_RESOURCES_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_CONFIG:
+            {
+                GObject *value_object = g_value_get_object (value);
+
+                if (priv->config)
+                    g_object_unref (priv->config);
+
+                if (value_object != NULL) {
+                    UfoConfig *config;
+                    GList *paths;
+
+                    config = UFO_CONFIG (value_object);
+                    paths = ufo_config_get_paths (config);
+                    priv->kernel_paths = g_list_concat (priv->kernel_paths, paths);
+                    g_list_foreach (paths, (GFunc) append_include_path, priv->include_paths);
+                    g_object_ref (config);
+                    priv->config = config;
+                }
+            }
+            break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_resources_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+    UfoResourcesPrivate *priv = UFO_RESOURCES_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_CONFIG:
+            g_value_set_object (value, priv->config);
+            break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_resources_dispose (GObject *object)
+{
+    UfoResourcesPrivate *priv = UFO_RESOURCES_GET_PRIVATE (object);
+
+    if (priv->config) {
+        g_object_unref (priv->config);
+        priv->config = NULL;
+    }
+
+    G_OBJECT_CLASS (ufo_resources_parent_class)->finalize (object);
+}
+
+static void
+ufo_resources_finalize (GObject *object)
+{
+    UfoResourcesPrivate *priv = UFO_RESOURCES_GET_PRIVATE (object);
+
+    g_hash_table_destroy (priv->opencl_programs);
+    g_list_foreach (priv->kernel_paths, (GFunc) g_free, NULL);
+    g_list_free (priv->kernel_paths);
+    g_list_foreach (priv->opencl_kernels, (GFunc) resources_release_kernel, NULL);
+    g_list_free (priv->opencl_kernels);
+
+    for (guint i = 0; i < priv->num_devices[0]; i++)
+        UFO_RESOURCES_CHECK_CLERR (clReleaseCommandQueue (priv->command_queues[i]));
+
+    UFO_RESOURCES_CHECK_CLERR (clReleaseContext (priv->opencl_context));
+
+    g_string_free (priv->opencl_build_options, TRUE);
+    g_string_free (priv->include_paths, TRUE);
+
+    for (guint i = 0; i < priv->num_platforms; i ++)
+        g_free (priv->opencl_devices[i]);
+
+    g_free (priv->num_devices);
+    g_free (priv->opencl_devices);
+    g_free (priv->opencl_platforms);
+    g_free (priv->command_queues);
+
+    priv->num_devices = NULL;
+    priv->opencl_kernels = NULL;
+    priv->opencl_devices = NULL;
+    priv->opencl_platforms = NULL;
+
+    G_OBJECT_CLASS (ufo_resources_parent_class)->finalize (object);
+    g_debug ("UfoResources: finalized");
+}
+
+static void
+ufo_resources_class_init (UfoResourcesClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->set_property = ufo_resources_set_property;
+    gobject_class->get_property = ufo_resources_get_property;
+    gobject_class->dispose      = ufo_resources_dispose;
+    gobject_class->finalize     = ufo_resources_finalize;
+
+    g_object_class_override_property (gobject_class, PROP_CONFIG, "config");
+
+    g_type_class_add_private (klass, sizeof (UfoResourcesPrivate));
+}
+
+static void
+ufo_resources_init (UfoResources *self)
+{
+    UfoResourcesPrivate *priv;
+    self->priv = priv = UFO_RESOURCES_GET_PRIVATE (self);
+
+    priv->config = NULL;
+    priv->opencl_programs = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                   g_free, (GDestroyNotify) resources_release_program);
+    priv->opencl_kernels = NULL;
+    priv->opencl_platforms = NULL;
+    priv->opencl_build_options = g_string_new ("-cl-mad-enable ");
+    priv->include_paths = g_string_new ("-I. ");
+
+    priv->kernel_paths = g_list_append (NULL, g_strdup ("."));
+    priv->kernel_paths = g_list_append (priv->kernel_paths, g_strdup (UFO_PLUGIN_DIR));
+
+    /* initialize OpenCL subsystem */
+    int errcode = CL_SUCCESS;
+    UFO_RESOURCES_CHECK_CLERR (clGetPlatformIDs (0, NULL, &priv->num_platforms));
+    priv->opencl_platforms = g_malloc0(priv->num_platforms * sizeof (cl_platform_id));
+
+    UFO_RESOURCES_CHECK_CLERR (clGetPlatformIDs (priv->num_platforms, priv->opencl_platforms, NULL));
+    priv->num_devices = g_malloc0(priv->num_platforms * sizeof (cl_uint));
+    priv->opencl_devices = g_malloc0(priv->num_platforms * sizeof (cl_device_id *));
+
+    /* Get devices for each available platform */
+    gchar *info_buffer = g_malloc0 (256);
+
+    for (guint i = 0; i < priv->num_platforms; i++) {
+        cl_uint num_devices;
+        cl_platform_id platform = priv->opencl_platforms[i];
+
+        UFO_RESOURCES_CHECK_CLERR (clGetPlatformInfo (platform, CL_PLATFORM_VENDOR, 256, info_buffer, NULL));
+
+        if (g_str_has_prefix (info_buffer, "NVIDIA"))
+            g_string_append (priv->opencl_build_options, "-cl-nv-verbose -DVENDOR=NVIDIA");
+        else if (g_str_has_prefix (info_buffer, "Advanced Micro Devices"))
+            g_string_append (priv->opencl_build_options, "-DVENDOR=AMD");
+
+        UFO_RESOURCES_CHECK_CLERR (clGetDeviceIDs (platform,
+                                                   CL_DEVICE_TYPE_ALL,
+                                                   0, NULL,
+                                                   &num_devices));
+        priv->opencl_devices[i] = g_malloc0 (num_devices * sizeof (cl_device_id));
+
+        UFO_RESOURCES_CHECK_CLERR (clGetDeviceIDs (platform,
+                                                   CL_DEVICE_TYPE_ALL,
+                                                   num_devices, priv->opencl_devices[i],
+                                                   NULL));
+        priv->num_devices[i] = num_devices;
+    }
+
+    g_free (info_buffer);
+    cl_command_queue_properties queue_properties = CL_QUEUE_PROFILING_ENABLE;
+
+    /* XXX: create context for each platform?! */
+    if (priv->num_platforms > 0) {
+        priv->opencl_context = clCreateContext (NULL,
+                                                priv->num_devices[0],
+                                                priv->opencl_devices[0],
+                                                NULL, NULL, &errcode);
+        UFO_RESOURCES_CHECK_CLERR (errcode);
+        priv->command_queues = g_malloc0 (priv->num_devices[0] * sizeof (cl_command_queue));
+
+        for (guint i = 0; i < priv->num_devices[0]; i++) {
+            priv->command_queues[i] = clCreateCommandQueue (priv->opencl_context,
+                                      priv->opencl_devices[0][i],
+                                      queue_properties, &errcode);
+            UFO_RESOURCES_CHECK_CLERR (errcode);
+        }
+    }
+}
diff --git a/ufo/ufo-resources.h b/ufo/ufo-resources.h
new file mode 100644
index 0000000..af465c8
--- /dev/null
+++ b/ufo/ufo-resources.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_RESOURCES_H
+#define __UFO_RESOURCES_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-config.h>
+#include <ufo/ufo-buffer.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_RESOURCES             (ufo_resources_get_type())
+#define UFO_RESOURCES(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_RESOURCES, UfoResources))
+#define UFO_IS_RESOURCES(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_RESOURCES))
+#define UFO_RESOURCES_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_RESOURCES, UfoResourcesClass))
+#define UFO_IS_RESOURCES_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_RESOURCES))
+#define UFO_RESOURCES_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_RESOURCES, UfoResourcesClass))
+
+#define UFO_RESOURCES_ERROR             ufo_resources_error_quark()
+
+typedef struct _UfoResources           UfoResources;
+typedef struct _UfoResourcesClass      UfoResourcesClass;
+typedef struct _UfoResourcesPrivate    UfoResourcesPrivate;
+
+
+typedef enum {
+    UFO_RESOURCES_ERROR_LOAD_PROGRAM,
+    UFO_RESOURCES_ERROR_CREATE_PROGRAM,
+    UFO_RESOURCES_ERROR_BUILD_PROGRAM,
+    UFO_RESOURCES_ERROR_CREATE_KERNEL
+} UfoResourcesError;
+
+/**
+ * UFO_RESOURCES_CHECK_CLERR:
+ * @error: OpenCL error code
+ *
+ * Check the return value of OpenCL functions and issue a warning with file and
+ * line number if an error occured.
+ */
+#define UFO_RESOURCES_CHECK_CLERR(error) { \
+    if ((error) != CL_SUCCESS) g_log("ocl", G_LOG_LEVEL_CRITICAL, "Error <%s:%i>: %s", __FILE__, __LINE__, ufo_resources_clerr((error))); }
+
+/**
+ * UfoResources:
+ *
+ * Manages OpenCL resources. The contents of the #UfoResources structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoResources {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoResourcesPrivate *priv;
+};
+
+/**
+ * UfoResourcesClass:
+ *
+ * #UfoResources class
+ */
+struct _UfoResourcesClass {
+    /*< private >*/
+    GObjectClass parent_class;
+};
+
+UfoResources   * ufo_resources_new                      (UfoConfig      *config);
+gpointer         ufo_resources_get_kernel               (UfoResources   *resources,
+                                                         const gchar    *filename,
+                                                         const gchar    *kernel,
+                                                         GError        **error);
+gpointer         ufo_resources_get_kernel_from_source   (UfoResources   *resources,
+                                                         const gchar    *source,
+                                                         const gchar    *kernel,
+                                                         GError        **error);
+gpointer         ufo_resources_get_context              (UfoResources   *resources);
+GList          * ufo_resources_get_cmd_queues           (UfoResources   *resources);
+const gchar    * ufo_resources_clerr                    (int             error);
+GType            ufo_resources_get_type                 (void);
+GQuark           ufo_resources_error_quark              (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-scheduler.c b/ufo/ufo-scheduler.c
new file mode 100644
index 0000000..4b39906
--- /dev/null
+++ b/ufo/ufo-scheduler.c
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef __APPLE__
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+
+#include <ufo/ufo-buffer.h>
+#include <ufo/ufo-config.h>
+#include <ufo/ufo-configurable.h>
+#include <ufo/ufo-cpu-task-iface.h>
+#include <ufo/ufo-gpu-task-iface.h>
+#include <ufo/ufo-remote-node.h>
+#include <ufo/ufo-remote-task.h>
+#include <ufo/ufo-resources.h>
+#include <ufo/ufo-scheduler.h>
+#include <ufo/ufo-task-node.h>
+#include <ufo/ufo-task-iface.h>
+
+/**
+ * SECTION:ufo-scheduler
+ * @Short_description: Schedule the execution of a graph of nodes
+ * @Title: UfoScheduler
+ *
+ * A scheduler object uses a graphs information to schedule the contained nodes
+ * on CPU and GPU hardware.
+ */
+
+G_DEFINE_TYPE_WITH_CODE (UfoScheduler, ufo_scheduler, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (UFO_TYPE_CONFIGURABLE, NULL))
+
+#define UFO_SCHEDULER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_SCHEDULER, UfoSchedulerPrivate))
+
+typedef struct {
+    UfoTask         *task;
+    UfoTaskMode      mode;
+    guint            n_inputs;
+    UfoInputParam   *in_params;
+    gboolean        *finished;
+} TaskLocalData;
+
+struct _UfoSchedulerPrivate {
+    UfoConfig       *config;
+    UfoResources    *resources;
+    GList           *remotes;
+    gboolean         expand;
+};
+
+enum {
+    PROP_0,
+    PROP_EXPAND,
+    PROP_REMOTES,
+    N_PROPERTIES,
+
+    /* Here come the overriden properties that we don't install ourselves. */
+    PROP_CONFIG,
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+/**
+ * ufo_scheduler_new:
+ * @config: A #UfoConfig or %NULL
+ * @remotes: (element-type utf8): A #GList with strings describing remote machines or %NULL
+ *
+ * Creates a new #UfoScheduler.
+ *
+ * Return value: A new #UfoScheduler
+ */
+UfoScheduler *
+ufo_scheduler_new (UfoConfig *config,
+                   GList *remotes)
+{
+    UfoScheduler *sched;
+    UfoSchedulerPrivate *priv;
+
+    sched = UFO_SCHEDULER (g_object_new (UFO_TYPE_SCHEDULER,
+                                         "config", config,
+                                         NULL));
+    priv = sched->priv;
+
+    for (GList *it = g_list_first (remotes); it != NULL; it = g_list_next (it))
+        priv->remotes = g_list_append (priv->remotes, g_strdup (it->data));
+
+    return sched;
+}
+
+/**
+ * ufo_scheduler_get_context:
+ * @scheduler: A #UfoScheduler
+ *
+ * Get the associated OpenCL context of @scheduler.
+ *
+ * Return value: (transfer full): An cl_context structure or %NULL on error.
+ */
+gpointer
+ufo_scheduler_get_context (UfoScheduler *scheduler)
+{
+    g_return_val_if_fail (UFO_IS_SCHEDULER (scheduler), NULL);
+    return ufo_resources_get_context (scheduler->priv->resources);
+}
+
+/**
+ * ufo_scheduler_set_task_expansion:
+ * @scheduler: A #UfoScheduler
+ * @split: %TRUE if task graph should be split
+ *
+ * Sets whether the task graph should be expanded before execution to increase
+ * multi GPU performance.
+ */
+void
+ufo_scheduler_set_task_expansion (UfoScheduler *scheduler,
+                                  gboolean expand)
+{
+    g_return_if_fail (UFO_IS_SCHEDULER (scheduler));
+    g_object_set (G_OBJECT (scheduler), "expand", expand, NULL);
+}
+
+static gboolean
+get_inputs (TaskLocalData *tld,
+            UfoBuffer **inputs)
+{
+    UfoTaskNode *node = UFO_TASK_NODE (tld->task);
+    guint n_finished = 0;
+
+    for (guint i = 0; i < tld->n_inputs; i++) {
+        UfoGroup *group;
+
+        if (!tld->finished[i]) {
+            UfoBuffer *input;
+
+            group = ufo_task_node_get_current_in_group (node, i);
+            input = ufo_group_pop_input_buffer (group, tld->task);
+
+            if (input == UFO_END_OF_STREAM) {
+                tld->finished[i] = TRUE;
+                n_finished++;
+            }
+            else
+                inputs[i] = input;
+        }
+        else
+            n_finished++;
+    }
+
+    return (tld->n_inputs == 0) || (n_finished < tld->n_inputs);
+}
+
+static void
+release_inputs (TaskLocalData *tld,
+                UfoBuffer **inputs)
+{
+    UfoTaskNode *node = UFO_TASK_NODE (tld->task);
+
+    for (guint i = 0; i < tld->n_inputs; i++) {
+        UfoGroup *group;
+
+        group = ufo_task_node_get_current_in_group (node, i);
+        ufo_group_push_input_buffer (group, tld->task, inputs[i]);
+        ufo_task_node_switch_in_group (node, i);
+    }
+}
+
+static void
+exchange_data (UfoBuffer *input,
+               TaskLocalData *tld)
+{
+    UfoRemoteNode *remote;
+    UfoGroup *group;
+    UfoBuffer *output;
+    UfoRequisition requisition;
+
+    remote = UFO_REMOTE_NODE (ufo_task_node_get_proc_node (UFO_TASK_NODE (tld->task)));
+    ufo_remote_node_send_inputs (remote, &input);
+    release_inputs (tld, &input);
+
+    ufo_remote_node_get_requisition (remote, &requisition);
+    group = ufo_task_node_get_out_group (UFO_TASK_NODE (tld->task));
+    output = ufo_group_pop_output_buffer (group, &requisition);
+    ufo_remote_node_get_result (remote, output);
+    ufo_group_push_output_buffer (group, output);
+}
+
+static void
+run_remote_task (TaskLocalData *tld)
+{
+    UfoRemoteNode *remote;
+    UfoBuffer *input;
+    guint n_remote_gpus;
+    GThreadPool *pool;
+    GError *error = NULL;
+
+    g_assert (tld->n_inputs == 1);
+    remote = UFO_REMOTE_NODE (ufo_task_node_get_proc_node (UFO_TASK_NODE (tld->task)));
+    n_remote_gpus = ufo_remote_node_get_num_gpus (remote);
+    pool = g_thread_pool_new ((GFunc) exchange_data, tld, (gint) n_remote_gpus, TRUE, &error);
+    g_assert_no_error (error);
+
+    /*
+     * We launch a new thread for each incoming input data set because then we
+     * can send as many items as we have remote GPUs available without waiting
+     * for processing to stop.
+     */
+    while (1) {
+        if (get_inputs (tld, &input))
+            g_thread_pool_push (pool, input, &error);
+        else
+            break;
+    }
+
+    g_thread_pool_free (pool, FALSE, TRUE);
+    ufo_group_finish (ufo_task_node_get_out_group (UFO_TASK_NODE (tld->task)));
+}
+
+static gpointer
+run_task (TaskLocalData *tld)
+{
+    UfoBuffer *inputs[tld->n_inputs];
+    UfoBuffer *output;
+    UfoTaskNode *node;
+    UfoRequisition requisition;
+    gboolean active;
+
+    node = UFO_TASK_NODE (tld->task);
+    active = TRUE;
+    output = NULL;
+
+    if (UFO_IS_REMOTE_TASK (tld->task)) {
+        run_remote_task (tld);
+        return NULL;
+    }
+
+    while (active) {
+        UfoGroup *group;
+
+        group = ufo_task_node_get_out_group (node);
+
+        /* Get input buffers */
+        active = get_inputs (tld, inputs);
+
+        if (!active) {
+            ufo_group_finish (group);
+            break;
+        }
+
+        /* Get output buffers */
+        ufo_task_get_requisition (tld->task, inputs, &requisition);
+
+        if (requisition.n_dims > 0) {
+            output = ufo_group_pop_output_buffer (group, &requisition);
+            g_assert (output != NULL);
+        }
+
+        /* Process */
+        if (UFO_IS_GPU_TASK (tld->task)) {
+            UfoGpuNode *gpu_node;
+
+            if (output != NULL)
+                ufo_buffer_discard_location (output, UFO_LOCATION_HOST);
+
+            gpu_node = UFO_GPU_NODE (ufo_task_node_get_proc_node (node));
+
+            switch (tld->mode) {
+                case UFO_TASK_MODE_SINGLE:
+                    active = ufo_gpu_task_process (UFO_GPU_TASK (tld->task),
+                                                   inputs, output,
+                                                   &requisition, gpu_node);
+                    break;
+
+                case UFO_TASK_MODE_GENERATE:
+                case UFO_TASK_MODE_REDUCE:
+                    do {
+                        ufo_gpu_task_process (UFO_GPU_TASK (tld->task),
+                                              inputs, output,
+                                              &requisition, gpu_node);
+                        release_inputs (tld, inputs);
+                        active = get_inputs (tld, inputs);
+                    } while (active);
+                    break;
+            }
+
+            if (tld->mode == UFO_TASK_MODE_REDUCE)
+                ufo_gpu_task_reduce (UFO_GPU_TASK (tld->task),
+                                     output,
+                                     &requisition,
+                                     gpu_node);
+        }
+        else if (UFO_IS_CPU_TASK (tld->task)) {
+            if (output != NULL)
+                ufo_buffer_discard_location (output, UFO_LOCATION_DEVICE);
+
+            switch (tld->mode) {
+                case UFO_TASK_MODE_SINGLE:
+                    active = ufo_cpu_task_process (UFO_CPU_TASK (tld->task), inputs, output, &requisition);
+                    break;
+
+                case UFO_TASK_MODE_GENERATE:
+                case UFO_TASK_MODE_REDUCE:
+                    do {
+                        ufo_cpu_task_process (UFO_CPU_TASK (tld->task),
+                                              inputs,
+                                              output,
+                                              &requisition);
+                        release_inputs (tld, inputs);
+                        active = get_inputs (tld, inputs);
+                    } while (active);
+                    break;
+            }
+
+            if (tld->mode == UFO_TASK_MODE_REDUCE)
+                ufo_cpu_task_reduce (UFO_CPU_TASK (tld->task), output, &requisition);
+        }
+
+        /* Release buffers for further consumption */
+        release_inputs (tld, inputs);
+
+        if (requisition.n_dims > 0) {
+            switch (tld->mode) {
+                case UFO_TASK_MODE_SINGLE:
+                    if (active)
+                        ufo_group_push_output_buffer (group, output);
+                    else
+                        ufo_group_finish (group);
+                    break;
+
+                case UFO_TASK_MODE_REDUCE:
+                    ufo_group_push_output_buffer (group, output);
+                    ufo_group_finish (group);
+                    break;
+
+                case UFO_TASK_MODE_GENERATE:
+                    {
+                        do {
+                            if (UFO_IS_GPU_TASK (tld->task)) {
+                                UfoGpuNode *gpu_node;
+
+                                ufo_buffer_discard_location (output, UFO_LOCATION_HOST);
+                                gpu_node = UFO_GPU_NODE (ufo_task_node_get_proc_node (node));
+                                active = ufo_gpu_task_generate (UFO_GPU_TASK (tld->task),
+                                                                output,
+                                                                &requisition, gpu_node);
+                            }
+                            else {
+                                active = ufo_cpu_task_generate (UFO_CPU_TASK (tld->task),
+                                                                output,
+                                                                &requisition);
+                            }
+
+                            if (active) {
+                                ufo_group_push_output_buffer (group, output);
+                                output = ufo_group_pop_output_buffer (group, &requisition);
+                            }
+                        } while (active);
+
+                        ufo_group_finish (group);
+                    }
+                    break;
+            }
+        }
+    }
+
+    g_message ("`%s' finished", G_OBJECT_TYPE_NAME (tld->task));
+
+    return NULL;
+}
+
+static void
+cleanup_task_local_data (TaskLocalData **tlds,
+                         guint n)
+{
+    for (guint i = 0; i < n; i++) {
+        TaskLocalData *tld = tlds[i];
+        g_free (tld->in_params);
+        g_free (tld->finished);
+        g_free (tld);
+    }
+
+    g_free (tlds);
+}
+
+static TaskLocalData **
+setup_tasks (UfoSchedulerPrivate *priv,
+             UfoTaskGraph *task_graph,
+             GError **error)
+{
+    TaskLocalData **tlds;
+    GList *nodes;
+    guint n_nodes;
+
+    nodes = ufo_graph_get_nodes (UFO_GRAPH (task_graph));
+    n_nodes = g_list_length (nodes);
+
+    tlds = g_new0 (TaskLocalData *, n_nodes);
+
+    for (guint i = 0; i < n_nodes; i++) {
+        UfoNode *node;
+        TaskLocalData *tld;
+
+        node = g_list_nth_data (nodes, i);
+        tld = g_new0 (TaskLocalData, 1);
+        tld->task = UFO_TASK (node);
+        tlds[i] = tld;
+
+        ufo_task_setup (UFO_TASK (node), priv->resources, error);
+        ufo_task_get_structure (UFO_TASK (node), &tld->n_inputs, &tld->in_params, &tld->mode);
+
+        tld->finished = g_new0 (gboolean, tld->n_inputs);
+
+        if (error && *error != NULL)
+            return NULL;
+    }
+
+    g_list_free (nodes);
+    return tlds;
+}
+
+static GList *
+setup_groups (UfoSchedulerPrivate *priv,
+              UfoTaskGraph *task_graph)
+{
+    GList *groups;
+    GList *nodes;
+    cl_context context;
+
+    groups = NULL;
+    nodes = ufo_graph_get_nodes (UFO_GRAPH (task_graph));
+    context = ufo_resources_get_context (priv->resources);
+
+    for (GList *it = g_list_first (nodes); it != NULL; it = g_list_next (it)) {
+        GList *successors;
+        UfoNode *node;
+        UfoGroup *group;
+        UfoSendPattern pattern;
+
+        node = UFO_NODE (it->data);
+        successors = ufo_graph_get_successors (UFO_GRAPH (task_graph), node);
+        pattern = ufo_task_node_get_send_pattern (UFO_TASK_NODE (node));
+
+        group = ufo_group_new (successors, context, pattern);
+        groups = g_list_append (groups, group);
+        ufo_task_node_set_out_group (UFO_TASK_NODE (node), group);
+
+        for (GList *jt = g_list_first (successors); jt != NULL; jt = g_list_next (jt)) {
+            UfoNode *target;
+            gpointer label;
+            guint input;
+
+            target = UFO_NODE (jt->data);
+            label = ufo_graph_get_edge_label (UFO_GRAPH (task_graph), node, target);
+            input = (guint) GPOINTER_TO_INT (label);
+            ufo_task_node_add_in_group (UFO_TASK_NODE (target), input, group);
+            ufo_group_set_num_expected (group, UFO_TASK (target),
+                                        ufo_task_node_get_num_expected (UFO_TASK_NODE (target),
+                                                                        input));
+        }
+
+        g_list_free (successors);
+    }
+
+    g_list_free (nodes);
+    return groups;
+}
+
+void
+ufo_scheduler_run (UfoScheduler *scheduler,
+                   UfoTaskGraph *task_graph,
+                   GError **error)
+{
+    UfoSchedulerPrivate *priv;
+    UfoArchGraph *arch_graph;
+    GList *groups;
+    guint n_nodes;
+    GThread **threads;
+    TaskLocalData **tlds;
+    GTimer *timer;
+
+    g_return_if_fail (UFO_IS_SCHEDULER (scheduler));
+    priv = scheduler->priv;
+
+    arch_graph = UFO_ARCH_GRAPH (ufo_arch_graph_new (priv->resources,
+                                                     priv->remotes));
+
+    if (priv->expand)
+        ufo_task_graph_expand (task_graph, arch_graph);
+
+    ufo_task_graph_map (task_graph, arch_graph);
+
+    /* Prepare task structures */
+    tlds = setup_tasks (priv, task_graph, error);
+
+    if (tlds == NULL)
+        return;
+
+    groups = setup_groups (priv, task_graph);
+    n_nodes = ufo_graph_get_num_nodes (UFO_GRAPH (task_graph));
+    threads = g_new0 (GThread *, n_nodes);
+    timer = g_timer_new ();
+
+    /* Spawn threads */
+    for (guint i = 0; i < n_nodes; i++) {
+        threads[i] = g_thread_create ((GThreadFunc) run_task, tlds[i], TRUE, error);
+
+        if (error && (*error != NULL))
+            return;
+    }
+
+    /* Wait for threads to finish */
+    for (guint i = 0; i < n_nodes; i++)
+        g_thread_join (threads[i]);
+
+    g_print ("Processing finished after %3.5fs\n", g_timer_elapsed (timer, NULL));
+    g_timer_destroy (timer);
+
+    /* Cleanup */
+    cleanup_task_local_data (tlds, n_nodes);
+    g_list_foreach (groups, (GFunc) g_object_unref, NULL);
+    g_list_free (groups);
+    g_free (threads);
+
+    g_object_unref (arch_graph);
+}
+
+static void
+copy_remote_list (UfoSchedulerPrivate *priv,
+                  GValueArray *array)
+{
+    if (priv->remotes != NULL) {
+        g_list_foreach (priv->remotes, (GFunc) g_free, NULL);
+        g_list_free (priv->remotes);
+        priv->remotes = NULL;
+    }
+
+    for (guint i = 0; i < array->n_values; i++) {
+        priv->remotes = g_list_append (priv->remotes,
+                                       g_strdup (g_value_get_string (g_value_array_get_nth (array, i))));
+    }
+}
+
+static void
+ufo_scheduler_set_property (GObject      *object,
+                            guint         property_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+    UfoSchedulerPrivate *priv = UFO_SCHEDULER_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_CONFIG:
+            {
+                GObject *vobject = g_value_get_object (value);
+
+                if (vobject != NULL) {
+                    if (priv->config != NULL)
+                        g_object_unref (priv->config);
+
+                    priv->config = UFO_CONFIG (vobject);
+                    g_object_ref (priv->config);
+                }
+            }
+            break;
+
+        case PROP_REMOTES:
+            copy_remote_list (priv, g_value_get_boxed (value));
+            break;
+
+        case PROP_EXPAND:
+            priv->expand = g_value_get_boolean (value);
+            break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_scheduler_get_property (GObject      *object,
+                            guint         property_id,
+                            GValue       *value,
+                            GParamSpec   *pspec)
+{
+    UfoSchedulerPrivate *priv = UFO_SCHEDULER_GET_PRIVATE (object);
+
+    switch (property_id) {
+        case PROP_EXPAND:
+            g_value_set_boolean (value, priv->expand);
+            break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+            break;
+    }
+}
+
+static void
+ufo_scheduler_constructed (GObject *object)
+{
+    UfoSchedulerPrivate *priv;
+
+    priv = UFO_SCHEDULER_GET_PRIVATE (object);
+    priv->resources = ufo_resources_new (priv->config);
+}
+
+static void
+ufo_scheduler_dispose (GObject *object)
+{
+    UfoSchedulerPrivate *priv;
+
+    priv = UFO_SCHEDULER_GET_PRIVATE (object);
+
+    if (priv->config != NULL) {
+        g_object_unref (priv->config);
+        priv->config = NULL;
+    }
+
+    if (priv->resources != NULL) {
+        g_object_unref (priv->resources);
+        priv->resources = NULL;
+    }
+
+    G_OBJECT_CLASS (ufo_scheduler_parent_class)->dispose (object);
+}
+
+static void
+ufo_scheduler_finalize (GObject *object)
+{
+    UfoSchedulerPrivate *priv;
+
+    priv = UFO_SCHEDULER_GET_PRIVATE (object);
+
+    g_list_foreach (priv->remotes, (GFunc) g_free, NULL);
+    g_list_free (priv->remotes);
+    priv->remotes = NULL;
+
+    G_OBJECT_CLASS (ufo_scheduler_parent_class)->finalize (object);
+}
+
+static void
+ufo_scheduler_class_init (UfoSchedulerClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    gobject_class->constructed  = ufo_scheduler_constructed;
+    gobject_class->set_property = ufo_scheduler_set_property;
+    gobject_class->get_property = ufo_scheduler_get_property;
+    gobject_class->dispose      = ufo_scheduler_dispose;
+    gobject_class->finalize     = ufo_scheduler_finalize;
+
+    properties[PROP_EXPAND] =
+        g_param_spec_boolean ("expand",
+                              "Expand the task graph for better multi GPU performance",
+                              "Expand the task graph for better multi GPU performance",
+                              TRUE,
+                              G_PARAM_READWRITE);
+
+    properties[PROP_REMOTES] =
+        g_param_spec_value_array ("remotes",
+                                  "List containing remote addresses",
+                                  "List containing remote addresses of machines running ufod",
+                                  g_param_spec_string ("remote",
+                                                       "A remote address in the form tcp://addr:port",
+                                                       "A remote address in the form tcp://addr:port (see http://api.zeromq.org/3-2:zmq-tcp)",
+                                                       ".",
+                                                       G_PARAM_READWRITE),
+                                  G_PARAM_READWRITE);
+
+    for (guint i = PROP_0 + 1; i < N_PROPERTIES; i++)
+        g_object_class_install_property (gobject_class, i, properties[i]);
+
+    g_object_class_override_property (gobject_class, PROP_CONFIG, "config");
+
+    g_type_class_add_private (klass, sizeof (UfoSchedulerPrivate));
+}
+
+static void
+ufo_scheduler_init (UfoScheduler *scheduler)
+{
+    UfoSchedulerPrivate *priv;
+
+    scheduler->priv = priv = UFO_SCHEDULER_GET_PRIVATE (scheduler);
+    priv->expand = TRUE;
+    priv->config = NULL;
+    priv->resources = NULL;
+    priv->remotes = NULL;
+}
diff --git a/ufo/ufo-scheduler.h b/ufo/ufo-scheduler.h
new file mode 100644
index 0000000..f3ae564
--- /dev/null
+++ b/ufo/ufo-scheduler.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_SCHEDULER_H
+#define __UFO_SCHEDULER_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-config.h>
+#include <ufo/ufo-task-graph.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_SCHEDULER             (ufo_scheduler_get_type())
+#define UFO_SCHEDULER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_SCHEDULER, UfoScheduler))
+#define UFO_IS_SCHEDULER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_SCHEDULER))
+#define UFO_SCHEDULER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_SCHEDULER, UfoSchedulerClass))
+#define UFO_IS_SCHEDULER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_SCHEDULER))
+#define UFO_SCHEDULER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_SCHEDULER, UfoSchedulerClass))
+
+typedef struct _UfoScheduler           UfoScheduler;
+typedef struct _UfoSchedulerClass      UfoSchedulerClass;
+typedef struct _UfoSchedulerPrivate    UfoSchedulerPrivate;
+
+/**
+ * UfoScheduler:
+ *
+ * The base class scheduler is responsible of assigning command queues to
+ * filters (thus managing GPU device resources) and decide if to run a GPU or a
+ * CPU. The actual schedule planning can be overriden.
+ */
+struct _UfoScheduler {
+    /*< private >*/
+    GObject parent_instance;
+
+    UfoSchedulerPrivate *priv;
+};
+
+/**
+ * UfoSchedulerClass:
+ *
+ * #UfoScheduler class
+ */
+struct _UfoSchedulerClass {
+    /*< private >*/
+    GObjectClass parent_class;
+};
+
+UfoScheduler* ufo_scheduler_new             (UfoConfig     *config,
+                                             GList         *remotes);
+void          ufo_scheduler_run             (UfoScheduler  *scheduler,
+                                             UfoTaskGraph  *task_graph,
+                                             GError**       error);
+gpointer      ufo_scheduler_get_context     (UfoScheduler  *scheduler);
+void          ufo_scheduler_set_task_expansion
+                                            (UfoScheduler  *scheduler,
+                                             gboolean       split);
+GType         ufo_scheduler_get_type        (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-task-graph.c b/ufo/ufo-task-graph.c
new file mode 100644
index 0000000..3fc0c79
--- /dev/null
+++ b/ufo/ufo-task-graph.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <json-glib/json-glib.h>
+#include <ufo/ufo-task-graph.h>
+#include <ufo/ufo-task-node.h>
+#include <ufo/ufo-remote-node.h>
+#include <ufo/ufo-cpu-task-iface.h>
+#include <ufo/ufo-gpu-task-iface.h>
+#include <ufo/ufo-input-task.h>
+#include <ufo/ufo-dummy-task.h>
+#include <ufo/ufo-remote-task.h>
+
+/**
+ * SECTION:ufo-task-graph
+ * @Short_description: Hold and manage #UfoTaskNode elements.
+ * @Title: UfoTaskGraph
+ */
+
+G_DEFINE_TYPE (UfoTaskGraph, ufo_task_graph, UFO_TYPE_GRAPH)
+
+#define UFO_TASK_GRAPH_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_TASK_GRAPH, UfoTaskGraphPrivate))
+
+struct _UfoTaskGraphPrivate {
+    UfoPluginManager *manager;
+    GHashTable *prop_sets;
+    GHashTable *json_nodes;
+    GList *remote_tasks;
+};
+
+typedef enum {
+    JSON_FILE,
+    JSON_DATA
+} JsonLocation;
+
+static void add_nodes_from_json     (UfoTaskGraph *, JsonNode *, GError **);
+static void handle_json_prop_set    (JsonObject *, const gchar *, JsonNode *, gpointer user);
+static void handle_json_single_prop (JsonObject *, const gchar *, JsonNode *, gpointer user);
+static void handle_json_task_edge   (JsonArray *, guint, JsonNode *, gpointer);
+static gboolean handle_json_task_node (JsonNode *, UfoTaskGraphPrivate *priv, GError **error);
+static void add_task_node_to_json_array (UfoNode *, JsonArray *);
+static JsonObject *json_object_from_ufo_node (UfoNode *node);
+static JsonNode *get_json_representation (UfoTaskGraph *, GError **);
+
+/**
+ * UfoTaskGraphError:
+ * @UFO_TASK_GRAPH_ERROR_JSON_KEY: Key is not found in JSON
+ *
+ * Task graph errors
+ */
+GQuark
+ufo_task_graph_error_quark (void)
+{
+    return g_quark_from_static_string ("ufo-task-graph-error-quark");
+}
+
+/**
+ * ufo_task_graph_new:
+ *
+ * Create a new task graph without any nodes.
+ *
+ * Returns: A #UfoGraph that can be upcast to a #UfoTaskGraph.
+ */
+UfoGraph *
+ufo_task_graph_new (void)
+{
+    UfoTaskGraph *graph;
+    graph = UFO_TASK_GRAPH (g_object_new (UFO_TYPE_TASK_GRAPH, NULL));
+    return UFO_GRAPH (graph);
+}
+
+static void
+read_json (UfoTaskGraph *graph,
+           UfoPluginManager *manager,
+           JsonLocation location,
+           const gchar *data,
+           GError **error)
+{
+    JsonParser *json_parser;
+    GError *tmp_error = NULL;
+
+    json_parser = json_parser_new ();
+
+    switch (location) {
+        case JSON_FILE:
+            json_parser_load_from_file (json_parser,
+                                        data,
+                                        &tmp_error);
+            break;
+
+        case JSON_DATA:
+            json_parser_load_from_data (json_parser,
+                                        data,
+                                        (gssize) strlen (data),
+                                        &tmp_error);
+            break;
+    }
+
+    if (tmp_error != NULL) {
+        g_propagate_prefixed_error (error, tmp_error, "Parsing JSON: ");
+        g_object_unref (json_parser);
+        return;
+    }
+
+    graph->priv->manager = manager;
+    g_object_ref (manager);
+
+    add_nodes_from_json (graph, json_parser_get_root (json_parser), error);
+    g_object_unref (json_parser);
+}
+
+/**
+ * ufo_task_graph_read_from_file:
+ * @graph: A #UfoTaskGraph.
+ * @manager: A #UfoPluginManager used to load the filters
+ * @filename: Path and filename to the JSON file
+ * @error: Indicates error in case of failed file loading or parsing
+ *
+ * Read a JSON configuration file to fill the structure of @graph.
+ */
+void
+ufo_task_graph_read_from_file (UfoTaskGraph *graph,
+                               UfoPluginManager *manager,
+                               const gchar *filename,
+                               GError **error)
+{
+    g_return_if_fail (UFO_IS_TASK_GRAPH (graph) &&
+                      UFO_IS_PLUGIN_MANAGER (manager) &&
+                      (filename != NULL));
+
+    read_json (graph, manager, JSON_FILE, filename, error);
+}
+
+/**
+ * ufo_task_graph_read_from_data:
+ * @graph: A #UfoTaskGraph.
+ * @manager: A #UfoPluginManager used to load the filters
+ * @json: %NULL-terminated string with JSON data
+ * @error: Indicates error in case of failed file loading or parsing
+ *
+ * Read a JSON configuration file to fill the structure of @graph.
+ */
+void
+ufo_task_graph_read_from_data (UfoTaskGraph *graph,
+                               UfoPluginManager *manager,
+                               const gchar *json,
+                               GError **error)
+{
+    g_return_if_fail (UFO_IS_TASK_GRAPH (graph) &&
+                      UFO_IS_PLUGIN_MANAGER (manager) &&
+                      (json != NULL));
+
+    read_json (graph, manager, JSON_DATA, json, error);
+}
+
+static JsonNode *
+get_json_representation (UfoTaskGraph *graph,
+                         GError **error)
+{
+    GList *task_nodes;
+    JsonNode *root_node = json_node_new (JSON_NODE_OBJECT);
+    JsonObject *root_object = json_object_new ();
+    JsonArray *nodes = json_array_new ();
+    JsonArray *edges = json_array_new ();
+
+    task_nodes = ufo_graph_get_nodes (UFO_GRAPH (graph));
+    g_list_foreach (task_nodes, (GFunc) add_task_node_to_json_array, nodes);
+
+    for (GList *it = g_list_first (task_nodes); it != NULL; it = g_list_next (it)) {
+        UfoNode *from;
+        GList *successors;
+
+        from = UFO_NODE (it->data);
+        successors = ufo_graph_get_successors (UFO_GRAPH (graph), from);
+
+        for (GList *jt = g_list_first (successors); jt != NULL; jt = g_list_next (jt)) {
+            UfoNode *to;
+            gint port;
+            JsonObject *to_object;
+            JsonObject *from_object;
+            JsonObject *edge_object;
+
+            to = UFO_NODE (jt->data);
+            port = GPOINTER_TO_INT (ufo_graph_get_edge_label (UFO_GRAPH (graph), from, to));
+            to_object  = json_object_from_ufo_node (to);
+            from_object = json_object_from_ufo_node (from);
+            edge_object = json_object_new ();
+
+            json_object_set_int_member (to_object, "input", port);
+            json_object_set_object_member (edge_object, "to", to_object);
+            json_object_set_object_member (edge_object, "from", from_object);
+            json_array_add_object_element (edges, edge_object);
+        }
+
+        g_list_free (successors);
+    }
+
+    json_object_set_array_member (root_object, "nodes", nodes);
+    json_object_set_array_member (root_object, "edges", edges);
+    json_node_set_object (root_node, root_object);
+    g_list_free (task_nodes);
+
+    return root_node;
+}
+
+/**
+ * ufo_task_graph_save_to_json:
+ * @graph: A #UfoTaskGraph.
+ * @filename: Path and filename to the JSON file
+ * @error: Indicates error in case of failed file saving
+ *
+ * Save a JSON configuration file with the filter structure of @graph.
+ */
+void
+ufo_task_graph_save_to_json (UfoTaskGraph *graph,
+                             const gchar *filename,
+                             GError **error)
+{
+    JsonNode *root_node;
+    JsonGenerator *generator;
+
+    root_node = get_json_representation (graph, error);
+
+    if (error != NULL && *error != NULL)
+        return;
+
+    generator = json_generator_new ();
+    json_generator_set_root (generator, root_node);
+    json_generator_to_file (generator, filename, error);
+
+    json_node_free (root_node);
+    g_object_unref (generator);
+}
+
+static gboolean
+is_gpu_task (UfoNode *node, gpointer user_data)
+{
+    return UFO_IS_GPU_TASK (node);
+}
+
+static UfoTaskNode *
+build_remote_graph (UfoTaskGraph *remote_graph,
+                    GList *first,
+                    GList *last)
+{
+    UfoTaskNode *node;
+    UfoTaskNode *predecessor = NULL;
+
+    for (GList *it = g_list_next (first); it != last; it = g_list_next (it)) {
+        node = UFO_TASK_NODE (it->data);
+
+        if (predecessor != NULL)
+            ufo_task_graph_connect_nodes (remote_graph, predecessor, node);
+
+        predecessor = node;
+    }
+
+    return node;
+}
+
+static void
+create_remote_tasks (UfoTaskGraph *task_graph,
+                     UfoTaskGraph *remote_graph,
+                     UfoTaskNode *first,
+                     UfoTaskNode *last,
+                     UfoRemoteNode *remote)
+{
+    UfoTaskGraphPrivate *priv;
+    UfoTaskNode *task;
+    JsonNode *root;
+    JsonGenerator *generator;
+    gchar *json;
+    gsize size;
+
+    root = get_json_representation (remote_graph, NULL);
+    generator = json_generator_new ();
+    json_generator_set_root (generator, root);
+    json = json_generator_to_data (generator, &size);
+
+    priv = task_graph->priv;
+    ufo_remote_node_send_json (remote, json, size);
+
+    task = UFO_TASK_NODE (ufo_remote_task_new ());
+    priv->remote_tasks = g_list_append (priv->remote_tasks, task);
+    ufo_task_node_set_proc_node (task, UFO_NODE (remote));
+
+    ufo_task_graph_connect_nodes (task_graph, first, task);
+    ufo_task_graph_connect_nodes (task_graph, task, last);
+
+    g_free (json);
+    json_node_free (root);
+    g_object_unref (generator);
+}
+
+static void
+expand_remotes (UfoTaskGraph *task_graph,
+                GList *remotes,
+                GList *path)
+{
+    UfoTaskGraph *remote_graph;
+    UfoTaskNode *node;
+    GList *first;
+    GList *last;
+
+    first = g_list_first (path);
+    last = g_list_last (path);
+    remote_graph = UFO_TASK_GRAPH (ufo_task_graph_new ());
+    node = build_remote_graph (remote_graph, first, last);
+
+    if (ufo_graph_get_num_nodes (UFO_GRAPH (remote_graph)) == 0) {
+        ufo_task_graph_connect_nodes (remote_graph,
+                                      UFO_TASK_NODE (ufo_dummy_task_new ()),
+                                      node);
+    }
+
+    for (GList *jt = g_list_first (remotes); jt != NULL; jt = g_list_next (jt)) {
+        create_remote_tasks (task_graph, remote_graph,
+                             first->data, last->data, jt->data);
+    }
+
+    g_object_unref (remote_graph);
+}
+
+static gboolean
+path_unvisited (GList *path,
+                GList **visited)
+{
+    GList *head;
+    GList *tail;
+
+    head = g_list_first (path);
+    tail = g_list_last (path);
+
+    for (GList *it = g_list_first (head); it != tail; it = g_list_next (it)) {
+        UfoNode *node = (UfoNode *) it->data;
+
+        if (g_list_find (*visited, node))
+            return FALSE;
+
+        *visited = g_list_append (*visited, node);
+    }
+
+    return TRUE;
+}
+
+static GList *
+remove_common_ancestry_paths (GList *paths)
+{
+    GList *result;
+    GList *visited;
+
+    result = NULL;
+    visited = NULL;
+
+    for (GList *it = g_list_first (paths); it != NULL; it = g_list_next (it)) {
+        GList *path = (GList *) it->data;
+
+        if (path_unvisited (it->data, &visited))
+            result = g_list_append (result, path);
+    }
+
+    g_list_free (visited);
+    g_list_free (paths);
+    return result;
+}
+
+static GList *
+find_longest_path (GList *paths)
+{
+    GList *longest = NULL;
+    guint max_length = 0;
+
+    for (GList *it = g_list_first (paths); it != NULL; it = g_list_next (it)) {
+        guint length;
+        GList *path;
+
+        path = (GList *) it->data;
+        length = g_list_length (path);
+
+        if (length > max_length) {
+            max_length = length;
+            longest = path;
+        }
+    }
+
+    return longest;
+}
+
+/**
+ * ufo_task_graph_expand:
+ * @task_graph: A #UfoTaskGraph
+ * @arch_graph: A #UfoArchGraph
+ *
+ * Expands @task_graph in a way that most of the resources in @arch_graph can be
+ * occupied. In the simple pipeline case, the longest possible GPU paths are
+ * duplicated as much as there are GPUs in @arch_graph.
+ */
+void
+ufo_task_graph_expand (UfoTaskGraph *task_graph,
+                      UfoArchGraph *arch_graph)
+{
+    GList *paths;
+    GList *path;
+
+    g_return_if_fail (UFO_IS_TASK_GRAPH (task_graph));
+
+    paths = ufo_graph_get_paths (UFO_GRAPH (task_graph), is_gpu_task);
+    g_debug ("Number of identified paths: %i", g_list_length (paths));
+    paths = remove_common_ancestry_paths (paths);
+    g_debug ("Number of cleaned paths: %i", g_list_length (paths));
+    path = find_longest_path (paths);
+
+    if (path != NULL) {
+        GList *remotes;
+        guint n_gpus;
+        guint n_remotes;
+
+        remotes = ufo_arch_graph_get_remote_nodes (arch_graph);
+        n_remotes = g_list_length (remotes);
+
+        if (n_remotes > 0) {
+            g_debug ("Expand for %i remote nodes", n_remotes);
+            expand_remotes (task_graph, remotes, path);
+        }
+
+        n_gpus = ufo_arch_graph_get_num_gpus (arch_graph);
+        g_debug ("Expand for %i GPU nodes", n_gpus);
+
+        for (guint i = 1; i < n_gpus; i++)
+            ufo_graph_expand (UFO_GRAPH (task_graph), path);
+
+        g_list_free (remotes);
+    }
+
+    g_list_foreach (paths, (GFunc) g_list_free, NULL);
+    g_list_free (paths);
+}
+
+/**
+ * ufo_task_graph_fuse:
+ * @task_graph: A #UfoTaskGraph
+ *
+ * Fuses task nodes to increase data locality.
+ *
+ * Note: This is not implemented and a no-op right now.
+ */
+void
+ufo_task_graph_fuse (UfoTaskGraph *task_graph)
+{
+}
+
+static void
+map_proc_node (UfoGraph *graph,
+               UfoNode *node,
+               guint proc_index,
+               GList *gpu_nodes)
+{
+    UfoNode *proc_node;
+    GList *successors;
+    guint n_gpus;
+
+    proc_node = UFO_NODE (g_list_nth_data (gpu_nodes, proc_index));
+
+    if ((UFO_IS_GPU_TASK (node) || UFO_IS_INPUT_TASK (node)) &&
+        (!ufo_task_node_get_proc_node (UFO_TASK_NODE (node)))) {
+
+        g_debug ("Mapping GPU %i to %s-%p",
+                 proc_index, G_OBJECT_TYPE_NAME (node),
+                 (gpointer) node);
+
+        ufo_task_node_set_proc_node (UFO_TASK_NODE (node), proc_node);
+    }
+
+    n_gpus = g_list_length (gpu_nodes);
+    successors = ufo_graph_get_successors (graph, node);
+
+    for (GList *it = g_list_first (successors); it != NULL; it = g_list_next (it)) {
+        map_proc_node (graph, UFO_NODE (it->data), proc_index, gpu_nodes);
+        proc_index = (proc_index + 1) % n_gpus;
+    }
+
+    g_list_free (successors);
+}
+
+
+/**
+ * ufo_task_graph_map:
+ * @task_graph: A #UfoTaskGraph
+ * @arch_graph: A #UfoArchGraph to which @task_graph's nodes are mapped onto
+ *
+ * Map task nodes of @task_graph to the processing nodes of @arch_graph. Not
+ * doing this could break execution of @task_graph.
+ */
+void
+ufo_task_graph_map (UfoTaskGraph *task_graph,
+                    UfoArchGraph *arch_graph)
+{
+    GList *gpu_nodes;
+    GList *roots;
+
+    gpu_nodes = ufo_arch_graph_get_gpu_nodes (arch_graph);
+    roots = ufo_graph_get_roots (UFO_GRAPH (task_graph));
+
+    for (GList *it = g_list_first (roots); it != NULL; it = g_list_next (it))
+        map_proc_node (UFO_GRAPH (task_graph), UFO_NODE (it->data), 0, gpu_nodes);
+
+    g_list_free (roots);
+    g_list_free (gpu_nodes);
+}
+
+/**
+ * ufo_task_graph_connect_nodes:
+ * @graph: A #UfoTaskGraph
+ * @n1: A source node
+ * @n2: A destination node
+ *
+ * Connect @n1 with @n2 using @n2's default input port. To specify any other
+ * port, use ufo_task_graph_connect_nodes_full().
+ */
+void
+ufo_task_graph_connect_nodes (UfoTaskGraph *graph,
+                              UfoTaskNode *n1,
+                              UfoTaskNode *n2)
+{
+    ufo_task_graph_connect_nodes_full (graph, n1, n2, 0);
+}
+
+/**
+ * ufo_task_graph_connect_nodes_full:
+ * @graph: A #UfoTaskGraph
+ * @n1: A source node
+ * @n2: A destination node
+ * @input: Input port of @n2
+ *
+ * Connect @n1 with @n2 using @n2's @input port.
+ */
+void
+ufo_task_graph_connect_nodes_full (UfoTaskGraph *graph,
+                                   UfoTaskNode *n1,
+                                   UfoTaskNode *n2,
+                                   guint input)
+{
+    ufo_graph_connect_nodes (UFO_GRAPH (graph), UFO_NODE (n1), UFO_NODE (n2), GINT_TO_POINTER (input));
+}
+
+static void
+add_nodes_from_json (UfoTaskGraph *graph,
+                     JsonNode *root,
+                     GError **error)
+{
+    JsonObject *root_object = json_node_get_object (root);
+
+    if (json_object_has_member (root_object, "prop-sets")) {
+        JsonObject *sets = json_object_get_object_member (root_object, "prop-sets");
+        json_object_foreach_member (sets, handle_json_prop_set, graph->priv);
+    }
+
+    if (json_object_has_member (root_object, "nodes")) {
+        JsonArray *nodes = json_object_get_array_member (root_object, "nodes");
+        GList *elements = json_array_get_elements (nodes);
+
+        for (GList *it = g_list_first (elements); it != NULL; it = g_list_next (it)) {
+            if (!handle_json_task_node (it->data, graph->priv, error)) {
+                g_list_free (elements);
+                return;
+            }
+        }
+
+        g_list_free (elements);
+
+        /*
+         * We only check edges if we have nodes, anything else doesn't make much
+         * sense.
+         */
+        if (json_object_has_member (root_object, "edges")) {
+            JsonArray *edges = json_object_get_array_member (root_object, "edges");
+            json_array_foreach_element (edges, handle_json_task_edge, graph);
+        }
+    }
+}
+
+static gboolean
+handle_json_task_node (JsonNode *element,
+                       UfoTaskGraphPrivate *priv,
+                       GError **error)
+{
+    UfoNode *plugin;
+    JsonObject *object;
+    GError *tmp_error = NULL;
+    const gchar *name;
+    const gchar *plugin_name;
+
+    object = json_node_get_object (element);
+
+    if (!json_object_has_member (object, "plugin") ||
+        !json_object_has_member (object, "name")) {
+        g_set_error (error, UFO_TASK_GRAPH_ERROR, UFO_TASK_GRAPH_ERROR_JSON_KEY,
+                     "Node does not have `plugin' or `name' key");
+        return FALSE;
+    }
+
+    plugin_name = json_object_get_string_member (object, "plugin");
+    plugin = ufo_plugin_manager_get_task (priv->manager, plugin_name, &tmp_error);
+
+    if (tmp_error != NULL) {
+        g_propagate_error (error, tmp_error);
+        return FALSE;
+    }
+
+    name = json_object_get_string_member (object, "name");
+
+    if (g_hash_table_lookup (priv->json_nodes, name) != NULL)
+        g_error ("Duplicate name `%s' found", name);
+
+    g_hash_table_insert (priv->json_nodes, g_strdup (name), plugin);
+
+    if (json_object_has_member (object, "properties")) {
+        JsonObject *prop_object = json_object_get_object_member (object, "properties");
+        json_object_foreach_member (prop_object, handle_json_single_prop, plugin);
+    }
+
+    if (json_object_has_member (object, "prop-refs")) {
+        JsonArray *prop_refs;
+
+        prop_refs = json_object_get_array_member (object, "prop-refs");
+
+        for (guint i = 0; i < json_array_get_length (prop_refs); i++) {
+            const gchar *ref_name = json_array_get_string_element (prop_refs, i);
+            JsonObject *prop_set = g_hash_table_lookup (priv->prop_sets, ref_name);
+
+            if (prop_set == NULL) {
+                g_warning ("No property set `%s' found in `prop-sets'", ref_name);
+            }
+            else {
+                json_object_foreach_member (prop_set,
+                                            handle_json_single_prop,
+                                            plugin);
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+static void
+handle_json_task_edge (JsonArray *array,
+                       guint index,
+                       JsonNode *element,
+                       gpointer user)
+{
+    UfoTaskGraph *graph = user;
+    UfoTaskGraphPrivate *priv = graph->priv;
+    JsonObject *edge;
+    UfoTaskNode *from_node, *to_node;
+    JsonObject *from_object, *to_object;
+    guint to_port;
+    const gchar *from_name;
+    const gchar *to_name;
+    GError *error = NULL;
+
+    edge = json_node_get_object (element);
+
+    if (!json_object_has_member (edge, "from") ||
+        !json_object_has_member (edge, "to")) {
+        g_error ("Edge does not have `from' or `to' key");
+        return;
+    }
+
+    /* Get from details */
+    from_object = json_object_get_object_member (edge, "from");
+
+    if (!json_object_has_member (from_object, "name")) {
+        g_error ("From node does not have `name' key");
+        return;
+    }
+
+    from_name = json_object_get_string_member (from_object, "name");
+
+    /* Get to details */
+    to_object = json_object_get_object_member (edge, "to");
+
+    if (!json_object_has_member (to_object, "name")) {
+        g_error ("To node does not have `name' key");
+        return;
+    }
+
+    to_name = json_object_get_string_member (to_object, "name");
+    to_port = 0;
+
+    if (json_object_has_member (to_object, "input"))
+        to_port = (guint) json_object_get_int_member (to_object, "input");
+
+    /* Get actual filters and connect them */
+    from_node = g_hash_table_lookup (priv->json_nodes, from_name);
+    to_node = g_hash_table_lookup (priv->json_nodes, to_name);
+
+    ufo_task_graph_connect_nodes_full (graph, from_node, to_node, to_port);
+
+    if (error != NULL)
+        g_warning ("%s", error->message);
+}
+
+static void
+handle_json_prop_set (JsonObject *object,
+                      const gchar *name,
+                      JsonNode *node,
+                      gpointer user)
+{
+    UfoTaskGraphPrivate *priv;
+    JsonObject *properties;
+
+    priv = (UfoTaskGraphPrivate *) user;
+    properties = json_object_get_object_member (object, name);
+    json_object_ref (properties);
+    g_hash_table_insert (priv->prop_sets, g_strdup (name), properties);
+}
+
+static void
+handle_json_single_prop (JsonObject *object,
+                         const gchar *name,
+                         JsonNode *node,
+                         gpointer user)
+{
+    GValue val = {0,};
+    json_node_get_value (node, &val);
+    g_object_set_property (G_OBJECT(user), name, &val);
+}
+
+static void
+add_task_node_to_json_array (UfoNode *node, JsonArray *array)
+{
+    JsonObject *node_object;
+    JsonNode *prop_node;
+
+    node_object = json_object_new ();
+
+    json_object_set_string_member (node_object,
+                                   "plugin",
+                                   ufo_task_node_get_plugin_name (UFO_TASK_NODE (node)));
+
+    json_object_set_string_member (node_object,
+                                   "name",
+                                   ufo_task_node_get_unique_name (UFO_TASK_NODE (node)));
+
+    prop_node = json_gobject_serialize (G_OBJECT (node));
+    json_object_set_member (node_object, "properties", prop_node);
+    json_array_add_object_element (array, node_object);
+}
+
+static JsonObject *
+json_object_from_ufo_node (UfoNode *node)
+{
+    JsonObject *object;
+
+    object = json_object_new ();
+    json_object_set_string_member (object,
+                                   "name",
+                                   ufo_task_node_get_unique_name (UFO_TASK_NODE (node)));
+    return object;
+}
+
+
+static void
+ufo_task_graph_dispose (GObject *object)
+{
+    UfoTaskGraphPrivate *priv;
+    GList *nodes;
+
+    priv = UFO_TASK_GRAPH_GET_PRIVATE (object);
+
+    if (priv->manager != NULL) {
+        g_object_unref (priv->manager);
+        priv->manager = NULL;
+    }
+
+    g_list_foreach (priv->remote_tasks, (GFunc) g_object_unref, NULL);
+    g_list_free (priv->remote_tasks);
+    priv->remote_tasks = NULL;
+
+    nodes = g_hash_table_get_values (priv->json_nodes);
+    g_list_foreach (nodes, (GFunc) g_object_unref, NULL);
+    g_list_free (nodes);
+
+    G_OBJECT_CLASS (ufo_task_graph_parent_class)->dispose (object);
+}
+
+static void
+ufo_task_graph_finalize (GObject *object)
+{
+    UfoTaskGraphPrivate *priv;
+
+    priv = UFO_TASK_GRAPH_GET_PRIVATE (object);
+
+    g_hash_table_destroy (priv->json_nodes);
+    g_hash_table_destroy (priv->prop_sets);
+
+    G_OBJECT_CLASS (ufo_task_graph_parent_class)->finalize (object);
+}
+
+static void
+ufo_task_graph_class_init (UfoTaskGraphClass *klass)
+{
+    GObjectClass *oclass;
+
+    oclass = G_OBJECT_CLASS (klass);
+    oclass->dispose = ufo_task_graph_dispose;
+    oclass->finalize = ufo_task_graph_finalize;
+
+    g_type_class_add_private(klass, sizeof(UfoTaskGraphPrivate));
+}
+
+static void
+ufo_task_graph_init (UfoTaskGraph *self)
+{
+    UfoTaskGraphPrivate *priv;
+    self->priv = priv = UFO_TASK_GRAPH_GET_PRIVATE (self);
+
+    priv->manager = NULL;
+    priv->remote_tasks = NULL;
+    priv->json_nodes = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                              g_free, NULL);
+
+    priv->prop_sets = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                             g_free, (GDestroyNotify) json_object_unref);
+
+    /* Maybe we should define a specific task node type from which all tasks
+     * must inherit */
+    ufo_graph_register_node_type (UFO_GRAPH (self), UFO_TYPE_NODE);
+}
diff --git a/ufo/ufo-task-graph.h b/ufo/ufo-task-graph.h
new file mode 100644
index 0000000..10b9c1a
--- /dev/null
+++ b/ufo/ufo-task-graph.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_TASK_GRAPH_H
+#define __UFO_TASK_GRAPH_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-graph.h>
+#include <ufo/ufo-arch-graph.h>
+#include <ufo/ufo-task-node.h>
+#include <ufo/ufo-plugin-manager.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_TASK_GRAPH             (ufo_task_graph_get_type())
+#define UFO_TASK_GRAPH(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_TASK_GRAPH, UfoTaskGraph))
+#define UFO_IS_TASK_GRAPH(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_TASK_GRAPH))
+#define UFO_TASK_GRAPH_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_TASK_GRAPH, UfoTaskGraphClass))
+#define UFO_IS_TASK_GRAPH_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_TASK_GRAPH))
+#define UFO_TASK_GRAPH_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_TASK_GRAPH, UfoTaskGraphClass))
+
+#define UFO_TASK_GRAPH_ERROR            ufo_task_graph_error_quark()
+
+typedef struct _UfoTaskGraph           UfoTaskGraph;
+typedef struct _UfoTaskGraphClass      UfoTaskGraphClass;
+typedef struct _UfoTaskGraphPrivate    UfoTaskGraphPrivate;
+
+
+typedef enum {
+    UFO_TASK_GRAPH_ERROR_JSON_KEY
+} UfoTaskGraphError;
+
+/**
+ * UfoTaskGraph:
+ *
+ * Main object for organizing filters. The contents of the #UfoTaskGraph structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoTaskGraph {
+    /*< private >*/
+    UfoGraph parent_instance;
+
+    UfoTaskGraphPrivate *priv;
+};
+
+/**
+ * UfoTaskGraphClass:
+ *
+ * #UfoTaskGraph class
+ */
+struct _UfoTaskGraphClass {
+    /*< private >*/
+    UfoGraphClass parent_class;
+};
+
+UfoGraph    *ufo_task_graph_new                 (void);
+void         ufo_task_graph_read_from_file      (UfoTaskGraph       *graph,
+                                                 UfoPluginManager   *manager,
+                                                 const gchar        *filename,
+                                                 GError            **error);
+void         ufo_task_graph_read_from_data      (UfoTaskGraph       *graph,
+                                                 UfoPluginManager   *manager,
+                                                 const gchar        *json,
+                                                 GError            **error);
+void         ufo_task_graph_save_to_json        (UfoTaskGraph       *graph,
+                                                 const gchar        *filename,
+                                                 GError            **error);
+void         ufo_task_graph_map                 (UfoTaskGraph       *task_graph,
+                                                 UfoArchGraph       *arch_graph);
+void         ufo_task_graph_expand              (UfoTaskGraph       *task_graph,
+                                                 UfoArchGraph       *arch_graph);
+void         ufo_task_graph_connect_nodes       (UfoTaskGraph       *graph,
+                                                 UfoTaskNode        *n1,
+                                                 UfoTaskNode        *n2);
+void         ufo_task_graph_connect_nodes_full  (UfoTaskGraph       *graph,
+                                                 UfoTaskNode        *n1,
+                                                 UfoTaskNode        *n2,
+                                                 guint               input);
+void         ufo_task_graph_fuse                (UfoTaskGraph       *task_graph);
+GType        ufo_task_graph_get_type            (void);
+GQuark       ufo_task_graph_error_quark         (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-task-iface.c b/ufo/ufo-task-iface.c
new file mode 100644
index 0000000..356064f
--- /dev/null
+++ b/ufo/ufo-task-iface.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ufo/ufo-task-iface.h>
+
+typedef UfoTaskIface UfoTaskInterface;
+
+G_DEFINE_INTERFACE (UfoTask, ufo_task, G_TYPE_OBJECT)
+
+/**
+ * UfoTaskError:
+ * @UFO_TASK_ERROR_SETUP: Error during setup of a task.
+ */
+GQuark
+ufo_task_error_quark ()
+{
+    return g_quark_from_static_string ("ufo-task-error-quark");
+}
+
+void
+ufo_task_setup (UfoTask *task,
+                UfoResources *resources,
+                GError **error)
+{
+    UFO_TASK_GET_IFACE (task)->setup (task, resources, error);
+}
+
+void
+ufo_task_get_requisition (UfoTask *task,
+                          UfoBuffer **inputs,
+                          UfoRequisition *requisition)
+{
+    UFO_TASK_GET_IFACE (task)->get_requisition (task, inputs, requisition);
+}
+
+void
+ufo_task_get_structure (UfoTask *task,
+                        guint *n_inputs,
+                        UfoInputParam **in_params,
+                        UfoTaskMode *mode)
+{
+    UFO_TASK_GET_IFACE (task)->get_structure (task, n_inputs, in_params, mode);
+}
+
+static void
+ufo_task_setup_real (UfoTask *task,
+                     UfoResources *resources,
+                     GError **error)
+{
+    g_warning ("`setup' not implemented");
+}
+
+static void
+ufo_task_get_requisition_real (UfoTask *task,
+                               UfoBuffer **inputs,
+                               UfoRequisition *requisition)
+{
+    g_warning ("`get_allocation' not implemented");
+}
+
+static void
+ufo_task_get_structure_real (UfoTask *task,
+                             guint *n_inputs,
+                             UfoInputParam **in_params,
+                             UfoTaskMode *mode)
+{
+    g_warning ("`get_structure' not implemented");
+}
+
+static void
+ufo_task_default_init (UfoTaskInterface *iface)
+{
+    iface->setup = ufo_task_setup_real;
+    iface->get_requisition = ufo_task_get_requisition_real;
+    iface->get_structure = ufo_task_get_structure_real;
+}
diff --git a/ufo/ufo-task-iface.h b/ufo/ufo-task-iface.h
new file mode 100644
index 0000000..1199627
--- /dev/null
+++ b/ufo/ufo-task-iface.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UFO_TASK_IFACE_H
+#define UFO_TASK_IFACE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-buffer.h>
+#include <ufo/ufo-resources.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_TASK             (ufo_task_get_type())
+#define UFO_TASK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_TASK, UfoTask))
+#define UFO_TASK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_TASK, UfoTaskIface))
+#define UFO_IS_TASK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_TASK))
+#define UFO_IS_TASK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_TASK))
+#define UFO_TASK_GET_IFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE((inst), UFO_TYPE_TASK, UfoTaskIface))
+
+#define UFO_TASK_ERROR            ufo_task_error_quark()
+
+typedef struct _UfoTask         UfoTask;
+typedef struct _UfoTaskIface    UfoTaskIface;
+typedef struct _UfoInputParam   UfoInputParam;
+
+typedef enum {
+    UFO_TASK_ERROR_SETUP
+} UfoTaskError;
+
+/**
+ * UfoTaskMode:
+ * @UFO_TASK_MODE_SINGLE: one-by-one processing
+ * @UFO_TASK_MODE_REDUCE: receive fininite stream and generate a reduced stream
+ * @UFO_TASK_MODE_GENERATE: do not receive any data but produce a stream.
+ *
+ * Task modes describe how a task operates considering the input data.
+ */
+typedef enum {
+    UFO_TASK_MODE_SINGLE,
+    UFO_TASK_MODE_REDUCE,
+    UFO_TASK_MODE_GENERATE
+} UfoTaskMode;
+
+/**
+ * UfoInputParam:
+ * @n_dims: Number of dimensions
+ */
+struct _UfoInputParam {
+    guint n_dims;
+};
+
+struct _UfoTaskIface {
+    /*< private >*/
+    GTypeInterface parent_iface;
+
+    void (*setup)           (UfoTask        *task,
+                             UfoResources   *resources,
+                             GError        **error);
+    void (*get_structure)   (UfoTask        *task,
+                             guint          *n_inputs,
+                             UfoInputParam **in_params,
+                             UfoTaskMode    *mode);
+    void (*get_requisition) (UfoTask        *task,
+                             UfoBuffer     **inputs,
+                             UfoRequisition *requisition);
+};
+
+void   ufo_task_setup           (UfoTask          *task,
+                                 UfoResources     *resources,
+                                 GError          **error);
+void   ufo_task_get_requisition (UfoTask          *task,
+                                 UfoBuffer       **inputs,
+                                 UfoRequisition   *requisition);
+void   ufo_task_get_structure   (UfoTask          *task,
+                                 guint            *n_inputs,
+                                 UfoInputParam  **in_params,
+                                 UfoTaskMode      *mode);
+
+GQuark ufo_task_error_quark     (void);
+GType  ufo_task_get_type        (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo-task-node.c b/ufo/ufo-task-node.c
new file mode 100644
index 0000000..6f60db6
--- /dev/null
+++ b/ufo/ufo-task-node.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <ufo/ufo-task-node.h>
+
+G_DEFINE_TYPE (UfoTaskNode, ufo_task_node, UFO_TYPE_NODE)
+
+#define UFO_TASK_NODE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_TASK_NODE, UfoTaskNodePrivate))
+
+
+struct _UfoTaskNodePrivate {
+    gchar           *plugin;
+    gchar           *unique;
+    UfoSendPattern   pattern;
+    UfoNode         *proc_node;
+    UfoGroup        *out_group;
+    GList           *in_groups[16];
+    GList           *current[16];
+    gint             n_expected[16];
+};
+
+void
+ufo_task_node_set_plugin_name (UfoTaskNode *task_node,
+                               const gchar *name)
+{
+    UfoTaskNodePrivate *priv;
+
+    g_return_if_fail (UFO_IS_TASK_NODE (task_node));
+    priv = task_node->priv;
+
+    g_free (priv->plugin);
+    priv->plugin = g_strdup (name);
+
+    g_free (priv->unique);
+    priv->unique = g_strdup_printf ("%s-%p", name, (gpointer) task_node);
+}
+
+const gchar *
+ufo_task_node_get_plugin_name (UfoTaskNode *task_node)
+{
+    g_return_val_if_fail (UFO_IS_TASK_NODE (task_node), NULL);
+    return task_node->priv->plugin;
+}
+
+const gchar *
+ufo_task_node_get_unique_name (UfoTaskNode *task_node)
+{
+    g_return_val_if_fail (UFO_IS_TASK_NODE (task_node), NULL);
+    return task_node->priv->unique;
+}
+
+void
+ufo_task_node_set_send_pattern (UfoTaskNode *node,
+                                UfoSendPattern pattern)
+{
+    g_return_if_fail (UFO_IS_TASK_NODE (node));
+    node->priv->pattern = pattern;
+}
+
+UfoSendPattern
+ufo_task_node_get_send_pattern (UfoTaskNode *node)
+{
+    g_return_val_if_fail (UFO_IS_TASK_NODE (node), 0);
+    return node->priv->pattern;
+}
+
+void
+ufo_task_node_set_num_expected (UfoTaskNode *node,
+                                guint pos,
+                                gint n_expected)
+{
+    g_return_if_fail (UFO_IS_TASK_NODE (node));
+    g_return_if_fail (pos < 16);
+    node->priv->n_expected[pos] = n_expected;
+}
+
+gint
+ufo_task_node_get_num_expected (UfoTaskNode *node,
+                                guint pos)
+{
+    g_return_val_if_fail (UFO_IS_TASK_NODE (node), 0);
+    g_return_val_if_fail (pos < 16, 0);
+    return node->priv->n_expected[pos];
+}
+
+void
+ufo_task_node_set_out_group (UfoTaskNode *node,
+                             UfoGroup *group)
+{
+    g_return_if_fail (UFO_IS_TASK_NODE (node));
+    node->priv->out_group = group;
+}
+
+/**
+ * ufo_task_node_get_out_group:
+ * @node: A #UfoTaskNode
+ *
+ * Get the current out of @node. The out group is used to fetch the ouput buffer
+ * for @node using ufo_group_pop_output_buffer().
+ *
+ * Return value: (transfer full): The out group of @node.
+ */
+UfoGroup *
+ufo_task_node_get_out_group (UfoTaskNode *node)
+{
+    g_return_val_if_fail (UFO_IS_TASK_NODE (node), NULL);
+    return node->priv->out_group;
+}
+
+void
+ufo_task_node_add_in_group (UfoTaskNode *node,
+                            guint pos,
+                            UfoGroup *group)
+{
+    g_return_if_fail (UFO_IS_TASK_NODE (node));
+    /* TODO: check out-of-bounds condition */
+    node->priv->in_groups[pos] = g_list_prepend (node->priv->in_groups[pos], group);
+    node->priv->current[pos] = node->priv->in_groups[pos];
+}
+
+/**
+ * ufo_task_node_get_current_in_group:
+ * @node: A #UfoTaskNode
+ * @pos: Input position of @node
+ *
+ * Several nodes can be connected to input @pos of @node. However, at a time
+ * @node will fetch only one buffer from all its inputs. This method returns the
+ * currently selected input group at @pos.
+ *
+ * Return value: (transfer full): The current in group of @node for @pos.
+ */
+UfoGroup *
+ufo_task_node_get_current_in_group (UfoTaskNode *node,
+                                    guint pos)
+{
+    g_return_val_if_fail (UFO_IS_TASK_NODE (node), NULL);
+    return UFO_GROUP (node->priv->current[pos]->data);
+}
+
+void
+ufo_task_node_switch_in_group (UfoTaskNode *node,
+                               guint pos)
+{
+    UfoTaskNodePrivate *priv;
+
+    g_return_if_fail (UFO_IS_TASK_NODE (node));
+    priv = node->priv;
+    priv->current[pos] = g_list_next (priv->current[pos]);
+
+    if (priv->current[pos] == NULL)
+        priv->current[pos] = g_list_first (priv->in_groups[pos]);
+}
+
+void
+ufo_task_node_set_proc_node (UfoTaskNode *task_node,
+                             UfoNode *proc_node)
+{
+    g_return_if_fail (UFO_IS_TASK_NODE (task_node) && UFO_IS_NODE (proc_node));
+    task_node->priv->proc_node = proc_node;
+}
+
+/**
+ * ufo_task_node_get_proc_node:
+ * @node: A #UfoTaskNode
+ *
+ * Get the associated processing node of @node.
+ *
+ * Return value: (transfer full): A #UfoNode.
+ */
+UfoNode *
+ufo_task_node_get_proc_node (UfoTaskNode *node)
+{
+    g_return_val_if_fail (UFO_IS_TASK_NODE (node), NULL);
+    return node->priv->proc_node;
+}
+
+static void
+ufo_task_node_dispose (GObject *object)
+{
+    G_OBJECT_CLASS (ufo_task_node_parent_class)->dispose (object);
+}
+
+static void
+ufo_task_node_finalize (GObject *object)
+{
+    UfoTaskNodePrivate *priv;
+
+    priv = UFO_TASK_NODE_GET_PRIVATE (object);
+    g_free (priv->plugin);
+    g_free (priv->unique);
+
+    G_OBJECT_CLASS (ufo_task_node_parent_class)->finalize (object);
+}
+
+static void
+ufo_task_node_class_init (UfoTaskNodeClass *klass)
+{
+    GObjectClass *oclass;
+
+    oclass = G_OBJECT_CLASS (klass);
+    oclass->dispose = ufo_task_node_dispose;
+    oclass->finalize = ufo_task_node_finalize;
+
+    g_type_class_add_private (klass, sizeof(UfoTaskNodePrivate));
+}
+
+static void
+ufo_task_node_init (UfoTaskNode *self)
+{
+    self->priv = UFO_TASK_NODE_GET_PRIVATE (self);
+    self->priv->plugin = NULL;
+    self->priv->unique = NULL;
+    self->priv->pattern = UFO_SEND_SCATTER;
+    self->priv->proc_node = NULL;
+    self->priv->out_group = NULL;
+
+    for (guint i = 0; i < 16; i++) {
+        self->priv->in_groups[i] = NULL;
+        self->priv->current[i] = NULL;
+        self->priv->n_expected[i] = -1;
+    }
+}
diff --git a/ufo/ufo-task-node.h b/ufo/ufo-task-node.h
new file mode 100644
index 0000000..2e6df01
--- /dev/null
+++ b/ufo/ufo-task-node.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_TASK_NODE_H
+#define __UFO_TASK_NODE_H
+
+#if !defined (__UFO_H_INSIDE__) && !defined (UFO_COMPILATION)
+#error "Only <ufo/ufo.h> can be included directly."
+#endif
+
+#include <ufo/ufo-node.h>
+#include <ufo/ufo-group.h>
+
+G_BEGIN_DECLS
+
+#define UFO_TYPE_TASK_NODE             (ufo_task_node_get_type())
+#define UFO_TASK_NODE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), UFO_TYPE_TASK_NODE, UfoTaskNode))
+#define UFO_IS_TASK_NODE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), UFO_TYPE_TASK_NODE))
+#define UFO_TASK_NODE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), UFO_TYPE_TASK_NODE, UfoTaskNodeClass))
+#define UFO_IS_TASK_NODE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), UFO_TYPE_TASK_NODE))
+#define UFO_TASK_NODE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), UFO_TYPE_TASK_NODE, UfoTaskNodeClass))
+
+typedef struct _UfoTaskNode           UfoTaskNode;
+typedef struct _UfoTaskNodeClass      UfoTaskNodeClass;
+typedef struct _UfoTaskNodePrivate    UfoTaskNodePrivate;
+
+
+/**
+ * UfoTaskNode:
+ *
+ * Main object for organizing filters. The contents of the #UfoTaskNode structure
+ * are private and should only be accessed via the provided API.
+ */
+struct _UfoTaskNode {
+    /*< private >*/
+    UfoNode parent_instance;
+
+    UfoTaskNodePrivate *priv;
+};
+
+/**
+ * UfoTaskNodeClass:
+ *
+ * #UfoTaskNode class
+ */
+struct _UfoTaskNodeClass {
+    /*< private >*/
+    UfoNodeClass parent_class;
+};
+
+void            ufo_task_node_set_plugin_name       (UfoTaskNode    *node,
+                                                     const gchar    *name);
+const gchar    *ufo_task_node_get_plugin_name       (UfoTaskNode    *node);
+const gchar    *ufo_task_node_get_unique_name       (UfoTaskNode    *node);
+void            ufo_task_node_set_send_pattern      (UfoTaskNode    *node,
+                                                     UfoSendPattern  pattern);
+UfoSendPattern  ufo_task_node_get_send_pattern      (UfoTaskNode    *node);
+void            ufo_task_node_set_num_expected      (UfoTaskNode    *node,
+                                                     guint           pos,
+                                                     gint            n_expected);
+gint            ufo_task_node_get_num_expected      (UfoTaskNode    *node,
+                                                     guint           pos);
+void            ufo_task_node_set_out_group         (UfoTaskNode    *node,
+                                                     UfoGroup       *group);
+UfoGroup       *ufo_task_node_get_out_group         (UfoTaskNode    *node);
+void            ufo_task_node_add_in_group          (UfoTaskNode    *node,
+                                                     guint           pos,
+                                                     UfoGroup       *group);
+UfoGroup       *ufo_task_node_get_current_in_group  (UfoTaskNode    *node,
+                                                     guint           pos);
+void            ufo_task_node_switch_in_group       (UfoTaskNode    *node,
+                                                     guint           pos);
+void            ufo_task_node_set_proc_node         (UfoTaskNode    *task_node,
+                                                     UfoNode        *proc_node);
+UfoNode        *ufo_task_node_get_proc_node         (UfoTaskNode    *node);
+GType           ufo_task_node_get_type              (void);
+
+G_END_DECLS
+
+#endif
diff --git a/ufo/ufo.h b/ufo/ufo.h
new file mode 100644
index 0000000..b9b3d8e
--- /dev/null
+++ b/ufo/ufo.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011-2013 Karlsruhe Institute of Technology
+ *
+ * This file is part of Ufo.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UFO_H
+#define __UFO_H
+
+#define __UFO_H_INSIDE__
+
+#include <ufo/ufo-arch-graph.h>
+#include <ufo/ufo-buffer.h>
+#include <ufo/ufo-config.h>
+#include <ufo/ufo-configurable.h>
+#include <ufo/ufo-cpu-node.h>
+#include <ufo/ufo-cpu-task-iface.h>
+#include <ufo/ufo-dummy-task.h>
+#include <ufo/ufo-enums.h>
+#include <ufo/ufo-gpu-node.h>
+#include <ufo/ufo-gpu-task-iface.h>
+#include <ufo/ufo-graph.h>
+#include <ufo/ufo-group.h>
+#include <ufo/ufo-input-task.h>
+#include <ufo/ufo-node.h>
+#include <ufo/ufo-output-task.h>
+#include <ufo/ufo-plugin-manager.h>
+#include <ufo/ufo-profiler.h>
+#include <ufo/ufo-remote-node.h>
+#include <ufo/ufo-remote-task.h>
+#include <ufo/ufo-resources.h>
+#include <ufo/ufo-scheduler.h>
+#include <ufo/ufo-task-graph.h>
+#include <ufo/ufo-task-iface.h>
+#include <ufo/ufo-task-node.h>
+
+#undef __UFO_H_INSIDE__
+
+#endif
diff --git a/ufo/ufo.pc.in b/ufo/ufo.pc.in
new file mode 100644
index 0000000..732f672
--- /dev/null
+++ b/ufo/ufo.pc.in
@@ -0,0 +1,13 @@
+prefix=@UFO_PKG_PREFIX@
+exec_prefix=@UFO_PKG_EXEC_PREFIX@
+libdir=@UFO_PKG_LIBDIR@
+includedir=@UFO_PKG_INCLUDEDIR@
+girdir=@UFO_PKG_GIRDIR@
+typelibdir=@UFO_PKG_TYPELIBDIR@
+
+Name: @TARNAME@
+Description: @UFO_DESCRIPTION_SUMMARY@
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lufo
+Cflags: -I${includedir}
+Requires: glib-2.0 gobject-2.0

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/ufo-core.git



More information about the debian-science-commits mailing list